{
  "openapi": "3.1.0",
  "info": {
    "title": "Callable API",
    "version": "0.1.0",
    "description": "Humans as a function for AI agents. Submit real-world tasks (phone calls, UGC video, form filling, account verification, authentic posting) to vetted human operators and receive structured results."
  },
  "servers": [
    { "url": "https://getcallable.dev" }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key"
      }
    },
    "schemas": {
      "PaymentCredential": {
        "description": "Stripe Link Agent Wallet SPT or x402 USDC credential. If not provided, the saved card on file for the developer's API key will be charged.",
        "oneOf": [
          { "type": "string", "description": "Shorthand: an SPT string, e.g. \"spt_xxx\"." },
          {
            "type": "object",
            "required": ["type"],
            "properties": {
              "type": { "type": "string", "enum": ["spt", "x402_usdc"] },
              "spt": { "type": "string", "description": "Stripe Shared Payment Token (when type = spt)." },
              "token": { "type": "string", "description": "x402 USDC authorization token (when type = x402_usdc)." },
              "network": { "type": "string", "description": "Settlement network (e.g. base) for x402_usdc." }
            }
          }
        ]
      },
      "TaskRequest": {
        "type": "object",
        "required": ["task_type", "instructions", "deadline_minutes"],
        "properties": {
          "task_type": {
            "type": "string",
            "enum": ["ugc_video", "phone_call", "account_verification", "form_filling", "authentic_posting", "lead_enrichment"],
            "description": "Type of task to assign to a human operator."
          },
          "instructions": {
            "type": "object",
            "description": "Structured, task_type-specific instructions. See /docs for per-type schemas."
          },
          "deadline_minutes": {
            "type": "integer",
            "minimum": 20,
            "maximum": 10080,
            "description": "Minutes the operator has to complete the task after claiming."
          },
          "callback_url": { "type": "string", "format": "uri", "description": "URL to POST the result to when the task is completed." },
          "metadata": { "type": "object", "additionalProperties": true },
          "priority": { "type": "string", "enum": ["normal", "urgent"] },
          "deadline": { "type": "string", "format": "date-time" },
          "operator_language": { "type": "string" },
          "payment_credential": { "$ref": "#/components/schemas/PaymentCredential" }
        }
      },
      "TaskResponse": {
        "type": "object",
        "required": ["task_id", "status"],
        "properties": {
          "task_id": { "type": "string", "format": "uuid", "description": "The task ID. Use this with GET /api/public/tasks/{task_id} to check status." },
          "status": { "type": "string", "example": "pending" },
          "payment": { "type": "string", "example": "confirmed" },
          "payment_method": { "type": "string", "enum": ["agent_wallet", "card"] },
          "amount_charged": { "type": "number" },
          "currency": { "type": "string" }
        }
      },
      "PaymentRequiredChallenge": {
        "type": "object",
        "properties": {
          "error": { "type": "string" },
          "payment": { "type": "string", "example": "required" },
          "challenge": {
            "type": "object",
            "properties": {
              "scheme": { "type": "string", "example": "mpp/stripe" },
              "version": { "type": "string", "example": "1" },
              "accepts": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "credential_type": { "type": "string" },
                    "network": { "type": "string" }
                  }
                }
              },
              "amount": { "type": "number" },
              "currency": { "type": "string" }
            }
          }
        }
      }
    }
  },
  "security": [{ "ApiKeyAuth": [] }],
  "paths": {
    "/api/public/tasks": {
      "post": {
        "summary": "Submit a task for human execution",
        "operationId": "createTask",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/TaskRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task accepted and payment confirmed.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/TaskResponse" }
              }
            }
          },
          "402": {
            "description": "Payment required. No payment_credential supplied and no saved card on file.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PaymentRequiredChallenge" }
              }
            }
          },
          "401": { "description": "Missing or invalid x-api-key." },
          "422": { "description": "Validation error in instructions or payload." }
        }
      }
    },
    "/api/public/tasks/{task_id}": {
      "get": {
        "summary": "Retrieve a task by id",
        "operationId": "getTask",
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "200": {
            "description": "Current task state.",
            "content": { "application/json": { "schema": { "type": "object" } } }
          },
          "404": { "description": "Task not found." }
        }
      }
    },
    "/api/public/tasks/{task_id}/review": {
      "post": {
        "summary": "Approve or dispute a completed task",
        "description": "Once a task is in `pending_review` status, the developer (agent) can approve the result to release payment to the operator, or dispute it with a reason and what is needed to accept it. Disputing reopens the operator's assignment so they can resubmit.",
        "operationId": "reviewTask",
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["decision"],
                "properties": {
                  "decision": { "type": "string", "enum": ["approve", "dispute"] },
                  "dispute_reason": { "type": "string", "description": "Required when decision = 'dispute'. What went wrong with the submission." },
                  "what_is_needed": { "type": "string", "description": "Required when decision = 'dispute'. What the operator needs to do for the submission to be accepted." }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Review applied.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "status": { "type": "string", "enum": ["completed", "disputed"] }
                  }
                }
              }
            }
          },
          "401": { "description": "Missing or invalid x-api-key." },
          "403": { "description": "Task does not belong to this developer." },
          "404": { "description": "Task not found." },
          "409": { "description": "Task is not in pending_review status." },
          "422": { "description": "Validation error in body." }
        }
      }
    }
  }
}
