{
  "openapi": "3.1.0",
  "info": {
    "title": "ProxAI Logistics API",
    "description": "Physical task execution API for AI agents operating in Quebec, Canada. Submit real-world tasks (inspections, pickups, auctions, IT support) to human operators via structured JSON endpoints. Tasks are paid in USDC (Solana or Base network) after human approval.\n\n**v4.0 Security & Performance (Phase 1 Roadmap):**\n- Centralized task index — O(1) list with pagination, filtering, and sort\n- Idempotency-Key support on all write endpoints (submit, review, complete)\n- Admin analytics dashboard — revenue by service/week, top clients, completion rate\n- SSRF protection on callback_url — requires HTTPS, blocks private IPs\n- Webhook replay attack prevention — timestamp included in HMAC signature\n- Rate limit storage errors return 503 (not 429)\n\n**Webhook Signature Verification (v4.0):**\nThe `X-ProxAI-Signature` header is computed as `HMAC-SHA256(secret, timestamp + '.' + rawBody)` where `timestamp` is the value of the `X-ProxAI-Timestamp` header. Receivers must verify both the signature and that the timestamp is within an acceptable window (e.g. 5 minutes) to prevent replay attacks.\n\n**Webhook Dead Letter Queue (DLQ):**\nAfter 3 failed delivery attempts (exponential backoff: 30s → 5min → 30min), undelivered events are stored in a Dead Letter Queue for 72 hours. A fallback email is sent to `requester_contact`. Retrieve failed events via `GET /api/webhooks/{webhook_id}/failures`.\n\n**Field Length Limits:**\n`task_description` max 2000 chars, `special_instructions` max 500 chars. Exceeding these returns HTTP 422 with error code `FIELD_TOO_LONG`.\n\n**Counter-Offer Flow:**\nWhen an operator proposes a different price, task status becomes `counter_offered`. The polling response includes `counter_offer_amount` (CAD) and `payment_usdc` (USDC equivalent). To accept: send the exact `payment_usdc` amount to the indicated wallet within 48 hours — payment constitutes implicit acceptance. No separate accept endpoint is needed.\n\n**Standard Error Codes:**\n`MISSING_FIELD` (400), `INVALID_FORMAT` (400), `FIELD_TOO_LONG` (422), `UNAUTHORIZED` (401), `RATE_LIMITED` (429), `STORAGE_ERROR` (503), `NOT_FOUND` (404), `CONFLICT` (409).",
    "version": "4.0.0",
    "contact": {
      "name": "ProxAI Logistics",
      "email": "admin@proxaiqc.com",
      "url": "https://www.proxaiqc.com"
    },
    "x-logo": {
      "url": "https://www.proxaiqc.com/logo.png"
    }
  },
  "servers": [
    {
      "url": "https://www.proxaiqc.com",
      "description": "Production"
    }
  ],
  "security": [],
  "paths": {
    "/api/submit-task": {
      "get": {
        "operationId": "getTaskStatus",
        "summary": "Check task status and retrieve deliverables",
        "description": "Retrieve the current status and full details of a submitted task. When status is `completed`, the response includes `proof_files_data` — an array of base64-encoded proof photos. Also returns USDC payment details when status is `accepted_pending_payment` or later. Every response includes a `_polling` object with `next_poll_seconds`, `is_terminal`, and `recommended_action` to guide agent polling behavior. Non-terminal responses also include a `Retry-After` HTTP header. Best practice: provide `callback_url` at submission to receive an instant webhook instead of polling.",
        "parameters": [
          {
            "name": "task_id",
            "in": "query",
            "description": "Task ID returned at submission (e.g. PROX-M5K2A1-B3C4D5)",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Task found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskStatusResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing task_id parameter"
          },
          "404": {
            "description": "Task not found"
          }
        }
      },
      "post": {
        "operationId": "submitTask",
        "summary": "Submit a physical task for execution",
        "description": "Submit a task requiring physical human presence in Quebec. A human operator reviews every submission. After approval, you receive USDC payment instructions. Task is executed once payment is confirmed on-chain. Deliverables (proof photos, reports) are sent to `requester_contact` and available via GET. Optionally provide a `callback_url` for webhook delivery upon completion.",
        "security": [
          {
            "ApiKeyHeader": []
          },
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Unique key to prevent duplicate task submissions. If a request with the same key was already processed, the cached response is returned with Idempotency-Replayed: true header. Keys expire after 24 hours.",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "X-ProxAI-Mode",
            "in": "header",
            "description": "Set to 'sandbox' to create a test task that simulates the full lifecycle without real execution. Sandbox tasks use SANDBOX- prefixed IDs, deterministic cost estimates, and auto-advance through statuses on each poll. No emails sent, no real payments.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "sandbox"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskSubmission"
              },
              "example": {
                "service_type": "SVC-01",
                "location_address": "123 Rue Principale, Laval, QC",
                "task_description": "Photograph all four exterior walls of the property and document visible damage.",
                "requester_contact": "agent@example.com",
                "urgency": "standard",
                "budget_limit": 300,
                "preferred_output": "JSON",
                "callback_url": "https://your-agent.example.com/webhooks/proxai",
                "lang": "en"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Task received successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid JSON body"
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "callbacks": {
          "taskCompleted": {
            "{$request.body#/callback_url}": {
              "post": {
                "summary": "Task completion webhook",
                "description": "When the task reaches `completed` status, ProxAI sends a POST to the callback_url provided at submission. This eliminates the need for polling. The payload includes the task record and links to retrieve proof files via the API.",
                "requestBody": {
                  "required": true,
                  "content": {
                    "application/json": {
                      "schema": {
                        "$ref": "#/components/schemas/WebhookPayload"
                      },
                      "example": {
                        "event": "task.completed",
                        "task_id": "PROX-M5K2A1-B3C4D5",
                        "status": "completed",
                        "service_type": "SVC-01",
                        "location_address": "123 Rue Principale, Laval, QC",
                        "completed_at": "2026-03-09T11:45:00Z",
                        "operator_notes": "All photos taken, no access issues.",
                        "proof_files": [
                          "https://www.proxaiqc.com/api/submit-task?task_id=PROX-M5K2A1-B3C4D5"
                        ],
                        "api_url": "https://www.proxaiqc.com/api/submit-task?task_id=PROX-M5K2A1-B3C4D5"
                      }
                    }
                  }
                },
                "responses": {
                  "200": {
                    "description": "Webhook received successfully"
                  },
                  "2XX": {
                    "description": "Any 2XX response is accepted"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/services": {
      "get": {
        "operationId": "listServices",
        "summary": "List all available services",
        "description": "Returns the full catalog of physical task services with IDs, descriptions, typical durations, required inputs, and output formats. No authentication required.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "Filter by service ID (e.g. SVC-01)",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "SVC-01",
                "SVC-02",
                "SVC-03",
                "SVC-04",
                "SVC-05"
              ]
            }
          },
          {
            "name": "contract",
            "in": "query",
            "description": "Set to 'false' to exclude contract details from response for lighter payloads. Default: include contracts.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Service catalog",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceList"
                }
              }
            }
          },
          "404": {
            "description": "Service not found"
          }
        }
      }
    },
    "/api/estimate": {
      "get": {
        "operationId": "getEstimate",
        "summary": "Calculate a cost estimate",
        "description": "Calculate an estimated task cost in CAD based on location and hours. Uses Google Distance Matrix to compute round-trip distance from Terrebonne hub. Formula: 50 + (site_hours × 100) + (transport_hours × 100) + (km_roundtrip × 0.90). No rounding — exact cents for unique USDC payment matching. Urgent tasks are multiplied by 1.25.",
        "parameters": [
          {
            "name": "location",
            "in": "query",
            "description": "Full address or city name (e.g. 'Laval, QC')",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "hours",
            "in": "query",
            "description": "Estimated task duration in hours (max 168 = 1 week)",
            "required": true,
            "schema": {
              "type": "number",
              "minimum": 0.25,
              "maximum": 168
            }
          },
          {
            "name": "urgency",
            "in": "query",
            "description": "standard or urgent (urgent adds 25% surcharge)",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "standard",
                "urgent"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cost estimate in CAD with breakdown",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Estimate"
                }
              }
            }
          },
          "422": {
            "description": "Validation error"
          }
        }
      }
    },
    "/api/api-keys": {
      "post": {
        "operationId": "createApiKey",
        "summary": "Request a new API key",
        "description": "Self-serve API key registration. Provide your agent/organization name and contact email. Returns a key with prefix `prox_`. Rate-limited to 3 keys per IP per hour.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiKeyRequest"
              },
              "example": {
                "name": "MyAgent v1",
                "contact": "agent@example.com"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "API key created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation error"
          },
          "429": {
            "description": "Rate limit: max 3 keys per IP per hour"
          }
        }
      },
      "get": {
        "operationId": "listApiKeys",
        "summary": "List your API keys",
        "description": "List all active API keys associated with your key. Requires API key authentication.",
        "security": [
          {
            "ApiKeyHeader": []
          },
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of API keys",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyList"
                }
              }
            }
          },
          "401": {
            "description": "Missing API key"
          }
        }
      },
      "delete": {
        "operationId": "revokeApiKey",
        "summary": "Revoke an API key",
        "description": "Permanently revoke an API key. Requires API key authentication.",
        "security": [
          {
            "ApiKeyHeader": []
          },
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "key_to_revoke"
                ],
                "properties": {
                  "key_to_revoke": {
                    "type": "string",
                    "description": "The full API key to revoke (e.g. prox_...)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Key revoked successfully"
          },
          "401": {
            "description": "Missing API key"
          },
          "404": {
            "description": "Key not found"
          }
        }
      }
    },
    "/api/webhooks": {
      "post": {
        "operationId": "registerWebhook",
        "summary": "Register a webhook URL",
        "description": "Register a webhook URL to receive event notifications. Requires API key authentication.",
        "security": [
          {
            "ApiKeyHeader": []
          },
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url",
                  "events"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "HTTPS webhook URL to receive events"
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Array of event types to subscribe to (e.g., ['task.completed', 'task.rejected'])"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook registered successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook_id": {
                      "type": "string",
                      "description": "Unique webhook identifier"
                    },
                    "secret": {
                      "type": "string",
                      "description": "Webhook signing secret for HMAC verification"
                    },
                    "url": {
                      "type": "string"
                    },
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid URL or events"
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "422": {
            "description": "Validation error"
          }
        }
      },
      "get": {
        "operationId": "listWebhooks",
        "summary": "List registered webhooks",
        "description": "List all registered webhook URLs for your API key. Requires API key authentication.",
        "security": [
          {
            "ApiKeyHeader": []
          },
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of registered webhooks",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhooks": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "webhook_id": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string"
                          },
                          "events": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          }
        }
      },
      "delete": {
        "operationId": "deleteWebhook",
        "summary": "Delete a webhook",
        "description": "Remove a registered webhook. Requires API key authentication.",
        "security": [
          {
            "ApiKeyHeader": []
          },
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "webhook_id"
                ],
                "properties": {
                  "webhook_id": {
                    "type": "string",
                    "description": "The webhook ID to delete"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook deleted successfully"
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "404": {
            "description": "Webhook not found"
          }
        }
      }
    },
    "/api/webhooks/{webhook_id}/failures": {
      "get": {
        "operationId": "getWebhookFailures",
        "summary": "List failed webhook deliveries (Dead Letter Queue)",
        "description": "Returns events that failed all 3 delivery attempts and are stored in the Dead Letter Queue. Events are retained for 72 hours.",
        "security": [
          {
            "ApiKeyHeader": []
          },
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "webhook_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Webhook ID returned at registration (e.g. wh_abc123)"
          }
        ],
        "responses": {
          "200": {
            "description": "List of failed webhook deliveries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook_id": {
                      "type": "string"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "failures": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "event_id": {
                            "type": "string"
                          },
                          "task_id": {
                            "type": "string"
                          },
                          "event": {
                            "type": "string"
                          },
                          "attempted_at": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "format": "date-time"
                            }
                          },
                          "last_http_status": {
                            "type": "integer",
                            "nullable": true
                          },
                          "payload": {
                            "type": "object"
                          },
                          "fallback_email_sent": {
                            "type": "boolean"
                          },
                          "expires_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "404": {
            "description": "Webhook not found"
          }
        }
      }
    },
    "/api/capabilities": {
      "get": {
        "operationId": "getCapabilities",
        "summary": "Platform capabilities manifest",
        "description": "Returns a comprehensive machine-readable manifest of all ProxAI capabilities: services, service zone (GeoJSON), operational hours, authentication, features (idempotency, webhooks, sandbox), endpoints, and quick-start guide. Designed for AI agent auto-discovery. Cached for 5 minutes.",
        "responses": {
          "200": {
            "description": "Capabilities manifest",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CapabilitiesManifest"
                }
              }
            }
          }
        }
      }
    },
    "/api/task-score": {
      "get": {
        "operationId": "getTaskScore",
        "summary": "Get score for a task or aggregate stats",
        "description": "Retrieve the rating for a specific task by task_id, or omit task_id to get aggregate reputation stats.",
        "parameters": [
          {
            "name": "task_id",
            "in": "query",
            "description": "Task ID. Omit to get aggregate stats.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Score or aggregate stats",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskScore"
                }
              }
            }
          },
          "404": {
            "description": "No score found for this task"
          }
        }
      },
      "post": {
        "operationId": "submitTaskScore",
        "summary": "Rate a completed task",
        "description": "Submit a 1-5 rating for a completed task. Each task can only be scored once. Requires API key authentication.",
        "security": [
          {
            "ApiKeyHeader": []
          },
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskScoreSubmission"
              },
              "example": {
                "task_id": "PROX-M5K2A1-B3C4D5",
                "rating": 5,
                "outcome": "completed",
                "feedback": "Photos delivered on time, excellent quality."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Score recorded"
          },
          "404": {
            "description": "Task not found"
          },
          "409": {
            "description": "Task already scored"
          },
          "422": {
            "description": "Validation error"
          }
        }
      }
    },
    "/api/mcp": {
      "post": {
        "operationId": "mcpEndpoint",
        "summary": "MCP Server (Streamable HTTP Transport)",
        "description": "Model Context Protocol server endpoint. Accepts JSON-RPC 2.0 requests per the MCP 2025-03-26 specification. Supports methods: initialize, tools/list, tools/call, ping. Stateless mode — no session management, ideal for AI agent integration. Authentication: Public tools (list_services, get_estimate, get_capabilities, get_reputation, register_api_key) require no auth. Write tools (submit_task, check_task_status, score_task, register_webhook, check_balance) require an API key via x-api-key header or api_key argument with the appropriate scope. Unauthenticated submit_task calls are forced into sandbox mode (non-billable).",
        "security": [
          {},
          { "ApiKeyHeader": [] },
          { "BearerAuth": [] }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jsonrpc",
                  "method"
                ],
                "properties": {
                  "jsonrpc": {
                    "type": "string",
                    "enum": [
                      "2.0"
                    ]
                  },
                  "id": {
                    "type": [
                      "string",
                      "integer"
                    ]
                  },
                  "method": {
                    "type": "string",
                    "enum": [
                      "initialize",
                      "tools/list",
                      "tools/call",
                      "ping",
                      "notifications/initialized"
                    ]
                  },
                  "params": {
                    "type": "object"
                  }
                }
              },
              "examples": {
                "initialize": {
                  "summary": "Initialize MCP session",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "initialize",
                    "params": {
                      "protocolVersion": "2025-03-26",
                      "capabilities": {},
                      "clientInfo": {
                        "name": "my-agent",
                        "version": "1.0"
                      }
                    }
                  }
                },
                "tools_list": {
                  "summary": "List available tools",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 2,
                    "method": "tools/list"
                  }
                },
                "tools_call": {
                  "summary": "Call a tool",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 3,
                    "method": "tools/call",
                    "params": {
                      "name": "list_services",
                      "arguments": {}
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC 2.0 response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jsonrpc": {
                      "type": "string"
                    },
                    "id": {
                      "type": [
                        "string",
                        "integer"
                      ]
                    },
                    "result": {
                      "type": "object"
                    },
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "integer"
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/tasks/stream": {
      "get": {
        "operationId": "streamTaskEvents",
        "summary": "Server-Sent Events stream for task updates (Phase 3)",
        "description": "Returns an SSE stream that emits events as a task moves through its lifecycle. Requires authentication: the API key that submitted the task, or the access_token returned at submission. Supports Last-Event-ID for reconnection. Stream auto-closes on terminal statuses (completed, rejected, failed). Event types: task.created, task.assigned, task.paid, task.on_site, task.completed, task.failed, stream.connected, stream.timeout.",
        "security": [
          { "ApiKeyHeader": [] },
          { "BearerAuth": [] }
        ],
        "parameters": [
          {
            "name": "task_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Task ID to stream updates for"
          },
          {
            "name": "access_token",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Access token from task submission response (alternative to API key for anonymous submissions)"
          }
        ],
        "responses": {
          "200": {
            "description": "SSE event stream",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Missing task_id parameter"
          },
          "403": {
            "description": "Authentication required — provide the API key or access_token that owns this task"
          },
          "404": {
            "description": "Task not found"
          }
        }
      }
    },
    "/api/billing": {
      "get": {
        "operationId": "getBillingInfo",
        "summary": "Check balance or transaction history (Phase 3)",
        "description": "action=balance: Real-time available balance. action=transactions: Filterable history by agent_id, date, service_type. Requires billing_read scope.",
        "security": [
          {
            "ApiKeyHeader": []
          },
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "action",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "balance",
                "transactions"
              ]
            },
            "description": "Action: balance or transactions"
          },
          {
            "name": "agent_id",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Agent identifier (email or key name)"
          },
          {
            "name": "date_from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Start date filter (YYYY-MM-DD)"
          },
          {
            "name": "date_to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "End date filter (YYYY-MM-DD)"
          },
          {
            "name": "service_type",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by service type (SVC-01..SVC-05)"
          }
        ],
        "responses": {
          "200": {
            "description": "Balance or transaction data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "agent_id": {
                      "type": "string"
                    },
                    "available_balance_cad": {
                      "type": "number",
                      "description": "Current available balance in CAD"
                    },
                    "spending_level": {
                      "type": "string",
                      "enum": [
                        "ok",
                        "warning",
                        "alert",
                        "blocked"
                      ],
                      "description": "Budget threshold status"
                    },
                    "transactions": {
                      "type": "array",
                      "description": "Present when action=transactions",
                      "items": {
                        "type": "object",
                        "properties": {
                          "task_id": {
                            "type": "string"
                          },
                          "amount_cad": {
                            "type": "number"
                          },
                          "service_type": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer",
                      "description": "Total transactions matching filters"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid action/agent_id parameter"
          },
          "401": {
            "description": "API key required with billing_read scope"
          },
          "429": {
            "description": "Rate limit exceeded"
          },
          "503": {
            "description": "Storage unavailable — retry after Retry-After header"
          }
        }
      },
      "post": {
        "operationId": "preAuthorizeCost",
        "summary": "Pre-authorize a task cost (Phase 3)",
        "description": "Check if the agent has sufficient budget for a task. Returns authorized: true/false with available balance details. If denied, includes reason and suggestions.",
        "security": [
          {
            "ApiKeyHeader": []
          },
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "max_cost_cad"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "pre_authorize"
                    ]
                  },
                  "agent_id": {
                    "type": "string",
                    "maxLength": 254,
                    "description": "Agent identifier (email or key name)"
                  },
                  "max_cost_cad": {
                    "type": "number",
                    "minimum": 0.01,
                    "description": "Maximum cost in CAD the agent is willing to pay"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cost authorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "authorized": {
                      "type": "boolean",
                      "description": "true if budget allows the cost"
                    },
                    "available_balance_cad": {
                      "type": "number"
                    },
                    "max_cost_cad": {
                      "type": "number"
                    },
                    "spending_level": {
                      "type": "string",
                      "enum": [
                        "ok",
                        "warning",
                        "alert",
                        "blocked"
                      ]
                    },
                    "valid_until": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Pre-authorization expires after 5 minutes"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid max_cost_cad"
          },
          "401": {
            "description": "API key required with billing_read scope"
          },
          "402": {
            "description": "Insufficient funds — includes reason and available balance"
          },
          "429": {
            "description": "Rate limit exceeded"
          },
          "503": {
            "description": "Storage unavailable"
          }
        }
      }
    },
    "/api/client-account": {
      "get": {
        "operationId": "getClientAccount",
        "summary": "Check own prepaid CAD account",
        "description": "Returns the prepaid CAD account linked to your API key's registered contact email. Shows balance, discount percentage, and account status. Admin keys can use ?email= to look up any account.",
        "tags": [
          "Client Account"
        ],
        "security": [
          {
            "ApiKeyHeader": []
          },
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "email",
            "in": "query",
            "description": "Admin only: look up a specific account by email.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include",
            "in": "query",
            "description": "Set to 'dashboard' to include transactions, tasks, and invoices (requires JWT auth)",
            "schema": {
              "type": "string",
              "enum": [
                "dashboard"
              ]
            }
          },
          {
            "name": "invoice_id",
            "in": "query",
            "description": "Specific invoice ID to retrieve full invoice data (requires JWT auth)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Account details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "account": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        },
                        "balance_cad": {
                          "type": "number"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "pending_deposit",
                            "active",
                            "suspended"
                          ]
                        },
                        "discount_percent": {
                          "type": "number"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No account found for this API key's contact email"
          }
        }
      },
      "post": {
        "operationId": "registerClientAccount",
        "summary": "Register a prepaid CAD account",
        "description": "Creates a new prepaid CAD account. The account email is automatically set to your API key's registered contact — you cannot register for someone else's email. Status starts as 'pending_deposit'. Contact admin@proxaiqc.com to arrange initial deposit via Interac e-Transfer. Once funded, tasks submitted with this API key will be auto-charged from the balance at approval time.",
        "tags": [
          "Client Account"
        ],
        "security": [
          {
            "ApiKeyHeader": []
          },
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Two modes: with API key (agent) requires only name+lang. Without API key (web form) requires full profile + password + CAPTCHA.",
                "required": [
                  "contact_name",
                  "email"
                ],
                "properties": {
                  "contact_name": {
                    "type": "string",
                    "description": "Full name of the contact person (min 2 chars)"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Account email address"
                  },
                  "password": {
                    "type": "string",
                    "minLength": 8,
                    "description": "Password (required for web registration, not needed with API key)"
                  },
                  "company_name": {
                    "type": "string",
                    "description": "Company name (optional)"
                  },
                  "phone": {
                    "type": "string",
                    "description": "Phone number (required for web registration)"
                  },
                  "address_line1": {
                    "type": "string",
                    "description": "Street address (required for web registration)"
                  },
                  "address_line2": {
                    "type": "string",
                    "description": "Address line 2 (optional)"
                  },
                  "city": {
                    "type": "string",
                    "description": "City (required for web registration)"
                  },
                  "province": {
                    "type": "string",
                    "default": "QC",
                    "description": "Province (default QC)"
                  },
                  "postal_code": {
                    "type": "string",
                    "description": "Postal code (required for web registration)"
                  },
                  "lang": {
                    "type": "string",
                    "enum": [
                      "fr",
                      "en"
                    ],
                    "default": "en",
                    "description": "Preferred language for communications"
                  },
                  "tos_accepted": {
                    "type": "boolean",
                    "description": "Terms of service accepted (required for web registration)"
                  },
                  "cf_turnstile_token": {
                    "type": "string",
                    "description": "Cloudflare Turnstile CAPTCHA token (required without API key)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Account created (status: pending_deposit)"
          },
          "409": {
            "description": "Account already exists for this email"
          }
        }
      }
    },
    "/api/client-auth": {
      "post": {
        "operationId": "clientAuth",
        "summary": "Client authentication (login, verify email, reset password)",
        "description": "Multi-action authentication endpoint for human clients with prepaid CAD accounts. Use `action` query parameter to select the operation. Login returns a JWT token (24h expiry) for accessing the client dashboard. Email verification and password reset use one-time tokens sent via email.",
        "tags": [
          "Client Account"
        ],
        "parameters": [
          {
            "name": "action",
            "in": "query",
            "description": "Authentication action: login (default), verify-email, reset-request, reset-confirm",
            "schema": {
              "type": "string",
              "enum": [
                "login",
                "verify-email",
                "reset-request",
                "reset-confirm"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields depend on action. login: email+password. verify-email: token. reset-request: email. reset-confirm: token+password.",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Account email (login, reset-request)"
                  },
                  "password": {
                    "type": "string",
                    "description": "Account password (login, reset-confirm)"
                  },
                  "token": {
                    "type": "string",
                    "description": "Verification or reset token (verify-email, reset-confirm)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Action successful",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "token": {
                      "type": "string",
                      "description": "JWT token (login action only, expires 24h)"
                    },
                    "org_id": {
                      "type": "string",
                      "description": "Organization ID (login action only)"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid credentials or token"
          },
          "422": {
            "description": "Missing required fields"
          },
          "429": {
            "description": "Rate limited (5 login attempts/hour per IP)"
          }
        }
      }
    },
    "/api/status": {
      "get": {
        "operationId": "getPlatformStatus",
        "summary": "Platform health check",
        "description": "Pre-flight health check for AI agents. Returns overall status, payment checker heartbeat, queue depth, and blockchain network info. Use before submitting costly tasks to verify ProxAI is operational. No authentication required.",
        "tags": [
          "System"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Platform health report",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "operational",
                        "degraded",
                        "partial_outage"
                      ],
                      "description": "Overall platform status"
                    },
                    "timestamp": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "latency_ms": {
                      "type": "integer",
                      "description": "Response time in milliseconds"
                    },
                    "services": {
                      "type": "object",
                      "properties": {
                        "api": {
                          "type": "string"
                        },
                        "storage": {
                          "type": "string",
                          "enum": [
                            "operational",
                            "down"
                          ]
                        },
                        "payment_checker": {
                          "type": "object",
                          "properties": {
                            "status": {
                              "type": "string",
                              "enum": [
                                "healthy",
                                "degraded",
                                "stale",
                                "never_run",
                                "unavailable"
                              ]
                            },
                            "last_run_at": {
                              "type": "string",
                              "format": "date-time",
                              "nullable": true
                            },
                            "minutes_ago": {
                              "type": "integer",
                              "nullable": true
                            },
                            "pending_checked": {
                              "type": "integer",
                              "nullable": true
                            }
                          }
                        }
                      }
                    },
                    "blockchain": {
                      "type": "object",
                      "properties": {
                        "networks": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "payment_currency": {
                          "type": "string"
                        }
                      }
                    },
                    "queue": {
                      "type": "object",
                      "properties": {
                        "pending_review": {
                          "type": "integer"
                        },
                        "pending_payment": {
                          "type": "integer"
                        },
                        "in_progress": {
                          "type": "integer"
                        },
                        "total_active": {
                          "type": "integer"
                        },
                        "avg_wait_minutes": {
                          "type": "integer",
                          "nullable": true
                        }
                      }
                    },
                    "issues": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "nullable": true
                    },
                    "suggested_action": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "API key via x-api-key header. Required for POST operations. Obtain via POST /api/api-keys or contact admin@proxaiqc.com."
      },
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key via Authorization: Bearer <key> header. Alternative to x-api-key."
      }
    },
    "schemas": {
      "TaskSubmission": {
        "type": "object",
        "required": [
          "service_type",
          "location_address",
          "task_description",
          "requester_contact"
        ],
        "properties": {
          "service_type": {
            "type": "string",
            "enum": [
              "SVC-01",
              "SVC-02",
              "SVC-03",
              "SVC-04",
              "SVC-05"
            ],
            "description": "Service type ID"
          },
          "location_address": {
            "type": "string",
            "maxLength": 300,
            "description": "Full street address of the task site in Quebec. Maximum 300 characters."
          },
          "task_description": {
            "type": "string",
            "maxLength": 2000,
            "description": "Clear, specific description of what needs to be done on-site. Maximum 2000 characters."
          },
          "requester_contact": {
            "type": "string",
            "format": "email",
            "maxLength": 254,
            "description": "Email for confirmation, payment instructions, and deliverable delivery"
          },
          "urgency": {
            "type": "string",
            "enum": [
              "standard",
              "urgent"
            ],
            "default": "standard",
            "description": "standard (72h execution) or urgent (24h, +25% surcharge)"
          },
          "budget_limit": {
            "type": "number",
            "description": "Maximum acceptable cost in CAD. Task will be counter-offered if estimate exceeds this.",
            "minimum": 0
          },
          "special_instructions": {
            "type": "string",
            "maxLength": 500,
            "description": "Access codes, contact persons, on-site requirements. Maximum 500 characters."
          },
          "preferred_output": {
            "type": "string",
            "maxLength": 100,
            "enum": [
              "photos",
              "PDF",
              "JSON",
              "CSV",
              "video"
            ],
            "description": "Desired deliverable format."
          },
          "callback_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Webhook URL for task completion notification. ProxAI will POST the full task record including proof_files_data to this URL when the task reaches `completed` status."
          },
          "lang": {
            "type": "string",
            "enum": [
              "fr",
              "en"
            ],
            "default": "fr",
            "description": "Language for client-facing emails (confirmation, payment, delivery, refund). Defaults to 'fr' (French). Set to 'en' for English."
          }
        }
      },
      "TaskResponse": {
        "type": "object",
        "properties": {
          "task_id": {
            "type": "string",
            "description": "Unique task identifier (e.g. PROX-M5K2A1-B3C4D5). Save this to track status."
          },
          "status": {
            "type": "string",
            "enum": [
              "received"
            ],
            "description": "Initial status. Transitions to accepted_pending_payment after human review."
          },
          "message": {
            "type": "string"
          },
          "review_deadline": {
            "type": "string"
          },
          "execution_deadline": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "_links": {
            "type": "object",
            "properties": {
              "self": {
                "type": "string"
              },
              "services": {
                "type": "string"
              },
              "estimate": {
                "type": "string"
              }
            }
          },
          "_polling": {
            "$ref": "#/components/schemas/PollingHint"
          }
        }
      },
      "TaskStatusResponse": {
        "type": "object",
        "properties": {
          "task_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "received",
              "accepted_pending_payment",
              "paid_ready",
              "in_progress",
              "completed",
              "rejected",
              "counter_offered",
              "payment_expired",
              "closed_test"
            ],
            "description": "Current lifecycle status. See status flow documentation."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "service_type": {
            "type": "string"
          },
          "location_address": {
            "type": "string"
          },
          "task_description": {
            "type": "string"
          },
          "urgency": {
            "type": "string"
          },
          "cost_breakdown": {
            "type": "object",
            "nullable": true,
            "description": "Cost breakdown set at acceptance: base_fee, site_hours, transport_hours, km_roundtrip, urgency_multiplier, total_cad",
            "properties": {
              "base_fee": {
                "type": "number"
              },
              "site_hours": {
                "type": "number"
              },
              "transport_hours": {
                "type": "number"
              },
              "km_roundtrip": {
                "type": "number"
              },
              "urgency_multiplier": {
                "type": "number"
              },
              "total_cad": {
                "type": "number"
              }
            }
          },
          "payment_usdc": {
            "type": "number",
            "nullable": true,
            "description": "Exact USDC amount to send. Present when status is accepted_pending_payment or counter_offered."
          },
          "payment_solana_address": {
            "type": "string",
            "nullable": true,
            "description": "Solana wallet address (pubkey) to send USDC to. Present when status is accepted_pending_payment."
          },
          "payment_base_address": {
            "type": "string",
            "nullable": true,
            "description": "Base network wallet address (0x...) to send USDC to. Present when status is accepted_pending_payment."
          },
          "payment_deadline": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "48-hour payment window deadline."
          },
          "payment_requested_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "payment_network": {
            "type": "string",
            "nullable": true,
            "description": "Network used for confirmed payment: solana or base."
          },
          "payment_tx": {
            "type": "string",
            "nullable": true,
            "description": "Blockchain transaction hash/signature of confirmed payment."
          },
          "payment_received_usdc": {
            "type": "number",
            "nullable": true,
            "description": "Actual USDC amount received on-chain."
          },
          "subtotal_cad": {
            "type": "number",
            "nullable": true,
            "description": "Task cost before taxes (HT). After discount if applicable. Set at approval."
          },
          "tps_amount": {
            "type": "number",
            "nullable": true,
            "description": "TPS tax amount (5% of subtotal_cad). Set at approval."
          },
          "tvq_amount": {
            "type": "number",
            "nullable": true,
            "description": "TVQ tax amount (9.975% of subtotal_cad). Set at approval."
          },
          "total_with_tax_cad": {
            "type": "number",
            "nullable": true,
            "description": "Total cost including taxes (TTC = subtotal_cad + tps_amount + tvq_amount). USDC payment_usdc is derived from this amount."
          },
          "proof_files_data": {
            "type": "array",
            "nullable": true,
            "description": "Present only when status is `completed`. Array of base64-encoded proof files delivered by the field operator.",
            "items": {
              "type": "object",
              "properties": {
                "filename": {
                  "type": "string"
                },
                "type": {
                  "type": "string",
                  "example": "image/jpeg",
                  "description": "MIME type of the file"
                },
                "content": {
                  "type": "string",
                  "description": "Base64-encoded file content"
                }
              }
            }
          },
          "counter_offer_amount": {
            "type": "number",
            "nullable": true,
            "description": "Revised CAD amount proposed by operator. Present when status is counter_offered. To accept, send the exact USDC amount shown in payment_usdc to the wallet address within 48 hours — payment is implicit acceptance. If not paid within the deadline, the task expires to payment_expired."
          },
          "review_message": {
            "type": "string",
            "nullable": true,
            "description": "Operator's message to client (approval notes, rejection reason, counter-offer explanation)."
          },
          "_payment": {
            "type": "object",
            "nullable": true,
            "description": "Detailed payment instructions. Present only when status is `accepted_pending_payment`. Contains wallet addresses, token info, and memo instructions for both Solana and Base L2 networks.",
            "properties": {
              "amount_usdc": {
                "type": "number",
                "description": "Exact USDC amount to send (matches payment_usdc). Calculated from total_cad (TTC)."
              },
              "subtotal_cad": {
                "type": "number",
                "description": "Task cost before taxes (HT)."
              },
              "tps": {
                "type": "number",
                "description": "TPS tax amount (5%)."
              },
              "tvq": {
                "type": "number",
                "description": "TVQ tax amount (9.975%)."
              },
              "total_cad": {
                "type": "number",
                "description": "Total including taxes (TTC)."
              },
              "deadline": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Payment deadline (48h window)."
              },
              "networks": {
                "type": "object",
                "properties": {
                  "solana": {
                    "type": "object",
                    "properties": {
                      "wallet": {
                        "type": "string",
                        "description": "Solana wallet address (public key) to send USDC to."
                      },
                      "token": {
                        "type": "string",
                        "description": "USDC SPL token mint address on Solana."
                      },
                      "memo": {
                        "type": "object",
                        "description": "Memo instruction details. Include task_id as memo for reliable matching.",
                        "properties": {
                          "recommended": {
                            "type": "boolean"
                          },
                          "value": {
                            "type": "string",
                            "description": "The task_id to include as memo."
                          },
                          "program": {
                            "type": "string",
                            "description": "Solana Memo Program ID."
                          },
                          "description": {
                            "type": "string"
                          },
                          "example_sdk": {
                            "type": "string",
                            "description": "JavaScript SDK example for adding memo instruction."
                          }
                        }
                      }
                    }
                  },
                  "base_l2": {
                    "type": "object",
                    "properties": {
                      "wallet": {
                        "type": "string",
                        "description": "Base L2 wallet address (0x...) to send USDC to."
                      },
                      "token": {
                        "type": "string",
                        "description": "USDC contract address on Base."
                      },
                      "memo": {
                        "type": "object",
                        "description": "Memo encoding details for Base L2. Encode task_id as UTF-8 hex in tx data field.",
                        "properties": {
                          "recommended": {
                            "type": "boolean"
                          },
                          "value": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "example": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              },
              "warning": {
                "type": "string",
                "description": "Important: mismatched memo causes payment to be ignored."
              }
            }
          },
          "_links": {
            "type": "object"
          },
          "_polling": {
            "$ref": "#/components/schemas/PollingHint"
          }
        }
      },
      "ApiKeyRequest": {
        "type": "object",
        "required": [
          "name",
          "contact"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 100,
            "description": "Agent or organization name"
          },
          "contact": {
            "type": "string",
            "format": "email",
            "maxLength": 254,
            "description": "Your email address"
          },
          "use_case": {
            "type": "string",
            "maxLength": 500,
            "description": "Optional description of your use case"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "audit_only",
                "task_create",
                "task_cancel",
                "billing_read"
              ]
            },
            "default": [
              "audit_only",
              "task_create"
            ],
            "description": "Permission scopes for the API key. Default: audit_only + task_create."
          }
        }
      },
      "ApiKeyResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "api_key": {
            "type": "string",
            "description": "Your API key. Store securely — shown only once.",
            "example": "prox_abc123..."
          },
          "key_name": {
            "type": "string"
          },
          "rate_limit": {
            "type": "string",
            "example": "60 requests/minute"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Granted permission scopes."
          },
          "warning": {
            "type": "string"
          }
        }
      },
      "ApiKeyList": {
        "type": "object",
        "properties": {
          "keys": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "role": {
                  "type": "string"
                },
                "contact": {
                  "type": "string"
                },
                "use_case": {
                  "type": "string",
                  "nullable": true
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          }
        }
      },
      "ServiceList": {
        "type": "object",
        "properties": {
          "services": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Service"
            }
          },
          "count": {
            "type": "integer"
          }
        }
      },
      "Service": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "deliverables": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "typical_duration_hours": {
            "type": "object",
            "properties": {
              "min": {
                "type": "number"
              },
              "max": {
                "type": "number"
              }
            }
          },
          "required_inputs": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "output_formats": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Estimate": {
        "type": "object",
        "properties": {
          "estimate": {
            "type": "object",
            "properties": {
              "subtotal_cad": {
                "type": "number",
                "description": "Subtotal before taxes (HT)."
              },
              "tps": {
                "type": "number",
                "description": "TPS amount (5% of subtotal)."
              },
              "tvq": {
                "type": "number",
                "description": "TVQ amount (9.975% of subtotal)."
              },
              "total_cad": {
                "type": "number",
                "description": "Total including taxes (TTC = subtotal + TPS + TVQ)."
              },
              "tax_rates": {
                "type": "object",
                "properties": {
                  "tps": {
                    "type": "number",
                    "example": 0.05
                  },
                  "tvq": {
                    "type": "number",
                    "example": 0.09975
                  }
                }
              },
              "breakdown": {
                "type": "object",
                "properties": {
                  "base_fee": {
                    "type": "number"
                  },
                  "hourly": {
                    "type": "number"
                  },
                  "mileage": {
                    "type": "number"
                  },
                  "urgency_surcharge": {
                    "type": "number"
                  }
                }
              },
              "currency": {
                "type": "string",
                "example": "CAD"
              },
              "distance_km_roundtrip": {
                "type": "number"
              },
              "distance_source": {
                "type": "string"
              }
            }
          },
          "formula": {
            "type": "string"
          },
          "note": {
            "type": "string"
          },
          "confidence_score": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Confidence level (0-1) based on distance source quality. google_maps=0.95, provided=0.90, distance_table=0.80, fallback/estimate=0.50."
          },
          "confidence_note": {
            "type": "string",
            "nullable": true,
            "description": "Human-readable note about estimate confidence."
          }
        }
      },
      "AgentError": {
        "type": "object",
        "description": "Agent-optimized error response. Every error includes machine-readable code, natural language message, and actionable suggestions.",
        "properties": {
          "error": {
            "type": "string",
            "description": "Machine-readable error code. Known values: VALIDATION_ERROR, FIELD_TOO_LONG, NOT_FOUND, RATE_LIMIT_EXCEEDED, UNAUTHORIZED, COST_EXCEEDS_LIMIT, DUPLICATE_KEY."
          },
          "message": {
            "type": "string",
            "description": "Human/LLM-readable explanation"
          },
          "reason": {
            "type": "string",
            "description": "Detailed technical reason for the error"
          },
          "suggested_alternatives": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Actionable suggestions the agent can try"
          },
          "details": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string"
                },
                "issue": {
                  "type": "string"
                }
              }
            },
            "description": "Per-field validation errors (for VALIDATION_ERROR)"
          },
          "docs_url": {
            "type": "string",
            "description": "Link to relevant documentation"
          }
        }
      },
      "ValidationError": {
        "$ref": "#/components/schemas/AgentError"
      },
      "CapabilitiesManifest": {
        "type": "object",
        "description": "Comprehensive platform capabilities manifest for AI agent auto-discovery.",
        "properties": {
          "platform": {
            "type": "object"
          },
          "services": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "service_zone": {
            "type": "object",
            "description": "GeoJSON Feature with service area polygon"
          },
          "known_locations": {
            "type": "object"
          },
          "operational_hours": {
            "type": "object"
          },
          "avg_response_times": {
            "type": "object"
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "currencies": {
            "type": "object"
          },
          "authentication": {
            "type": "object"
          },
          "features": {
            "type": "object",
            "description": "Idempotency, webhooks, sandbox capabilities"
          },
          "endpoints": {
            "type": "object"
          },
          "quick_start": {
            "type": "object"
          }
        }
      },
      "TaskScore": {
        "type": "object",
        "properties": {
          "task_id": {
            "type": "string"
          },
          "rating": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5
          },
          "outcome": {
            "type": "string",
            "enum": [
              "completed",
              "partial",
              "failed",
              "cancelled"
            ]
          },
          "feedback": {
            "type": "string",
            "nullable": true
          },
          "scored_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Invoice": {
        "type": "object",
        "description": "Tax invoice with TPS/TVQ breakdown. Generated automatically for deposits, task payments, and refunds.",
        "properties": {
          "invoice_id": {
            "type": "string",
            "description": "Sequential invoice number (e.g. PROX-INV-000001)"
          },
          "org_id": {
            "type": "string"
          },
          "task_id": {
            "type": "string",
            "nullable": true,
            "description": "Associated task ID (null for deposit receipts)"
          },
          "type": {
            "type": "string",
            "enum": [
              "deposit_receipt",
              "task_invoice",
              "refund_credit"
            ]
          },
          "subtotal_cad": {
            "type": "number",
            "description": "Amount before taxes (HT)"
          },
          "tps_amount": {
            "type": "number",
            "description": "TPS tax (5%)"
          },
          "tvq_amount": {
            "type": "number",
            "description": "TVQ tax (9.975%)"
          },
          "total_cad": {
            "type": "number",
            "description": "Total including taxes (TTC)"
          },
          "issued_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TaskScoreSubmission": {
        "type": "object",
        "required": [
          "task_id",
          "rating"
        ],
        "properties": {
          "task_id": {
            "type": "string",
            "maxLength": 30,
            "description": "Task identifier (e.g. PROX-M5K2A1-B3C4D5)"
          },
          "rating": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5
          },
          "outcome": {
            "type": "string",
            "enum": [
              "completed",
              "partial",
              "failed",
              "cancelled"
            ],
            "default": "completed"
          },
          "feedback": {
            "type": "string",
            "maxLength": 1000,
            "description": "Optional text feedback about the task execution"
          }
        }
      },
      "WebhookPayload": {
        "type": "object",
        "description": "Payload sent to callback_url when a task reaches completed status. Sent as a POST request with Content-Type: application/json. Timeout: 10 seconds. The proof_files field contains API URLs — call GET on api_url to retrieve base64-encoded proof_files_data.",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "task.completed"
            ],
            "description": "Event type. Currently only task.completed is supported."
          },
          "task_id": {
            "type": "string",
            "description": "Task identifier"
          },
          "status": {
            "type": "string",
            "enum": [
              "completed"
            ]
          },
          "service_type": {
            "type": "string",
            "description": "Service type ID (SVC-01 through SVC-05)"
          },
          "location_address": {
            "type": "string",
            "description": "Task site address"
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 completion timestamp"
          },
          "operator_notes": {
            "type": "string",
            "nullable": true,
            "description": "Notes from the field operator"
          },
          "proof_files": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            },
            "description": "API URLs to retrieve proof files. Call GET on these URLs and read proof_files_data for base64-encoded content."
          },
          "api_url": {
            "type": "string",
            "format": "uri",
            "description": "Direct API URL to fetch full task record including base64 proof files."
          }
        }
      },
      "PollingHint": {
        "type": "object",
        "description": "Polling guidance included in every GET /api/submit-task and POST /api/submit-task response. Agents should respect next_poll_seconds and stop polling when is_terminal is true. Best practice: use callback_url instead of polling.",
        "properties": {
          "is_terminal": {
            "type": "boolean",
            "description": "If true, the task has reached a final status (completed, rejected, closed_test, payment_expired). Stop polling."
          },
          "next_poll_seconds": {
            "type": "integer",
            "nullable": true,
            "description": "Recommended seconds to wait before next poll. null for terminal statuses. Values: received=60, accepted_pending_payment=30, counter_offered=120, paid_ready=60, in_progress=120."
          },
          "recommended_action": {
            "type": "string",
            "nullable": true,
            "description": "Human-readable instruction for what the agent should do next at this status."
          },
          "tip": {
            "type": "string",
            "description": "Hint encouraging use of callback_url for webhook delivery instead of polling."
          }
        }
      }
    }
  }
}