{
  "openapi": "3.0.3",
  "info": {
    "title": "Flowplane API",
    "description": "Flowplane workflow orchestration control plane API",
    "version": "1.0.0"
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT or API Key"
      }
    },
    "schemas": {}
  },
  "paths": {
    "/health": {
      "get": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/webhooks/clerk": {
      "post": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/webhooks/stripe": {
      "post": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/surfaces/email/act": {
      "get": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/surfaces/slack/interactivity": {
      "post": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/surfaces/slack/events": {
      "post": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/surfaces/discord/interactions": {
      "post": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/integrations/discord/callback": {
      "get": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/integrations/slack/callback": {
      "get": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/worker/register": {
      "post": {
        "summary": "Register (upsert) a workflow definition",
        "tags": [
          "Worker"
        ],
        "description": "SDK/worker self-registration. Idempotent upsert keyed by workflow id for the API key’s org.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "minLength": 1
                  },
                  "name": {
                    "type": "string"
                  },
                  "version": {
                    "type": "integer",
                    "exclusiveMinimum": true,
                    "minimum": 0,
                    "default": 1
                  },
                  "engine": {
                    "type": "string",
                    "enum": [
                      "bundled",
                      "inngest",
                      "temporal",
                      "trigger"
                    ],
                    "default": "inngest"
                  },
                  "steps": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "minLength": 1
                        },
                        "dependsOn": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "retryPolicy": {
                          "type": "object",
                          "properties": {
                            "maxAttempts": {
                              "type": "integer",
                              "exclusiveMinimum": true,
                              "minimum": 0
                            },
                            "backoff": {
                              "type": "string",
                              "enum": [
                                "fixed",
                                "exponential"
                              ]
                            },
                            "initialDelayMs": {
                              "type": "integer",
                              "exclusiveMinimum": true,
                              "minimum": 0
                            }
                          },
                          "required": [
                            "maxAttempts",
                            "backoff",
                            "initialDelayMs"
                          ],
                          "additionalProperties": false
                        }
                      },
                      "required": [
                        "id"
                      ],
                      "additionalProperties": false
                    }
                  }
                },
                "required": [
                  "id",
                  "steps"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/worker/decisions": {
      "post": {
        "summary": "Open a HITL decision (decision-first model)",
        "tags": [
          "Worker"
        ],
        "description": "Called by an engine adapter (e.g. flowplane.awaitApproval) to request a human decision for an external-engine run. The policy gate may resolve \"not required\", in which case approvalNeeded=false and no decision is created.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "engine": {
                    "type": "string",
                    "enum": [
                      "bundled",
                      "inngest",
                      "temporal",
                      "trigger",
                      "mcp"
                    ],
                    "default": "inngest"
                  },
                  "externalRunId": {
                    "type": "string",
                    "minLength": 1
                  },
                  "stepRef": {
                    "type": "string",
                    "minLength": 1
                  },
                  "workflowRef": {
                    "type": "string"
                  },
                  "assigneeEmail": {
                    "type": "string",
                    "format": "email"
                  },
                  "policy": {
                    "type": "string"
                  },
                  "required": {
                    "type": "boolean"
                  },
                  "context": {
                    "type": "object",
                    "properties": {
                      "prompt": {
                        "type": "string",
                        "maxLength": 4000,
                        "nullable": true
                      },
                      "aiReasoning": {
                        "type": "string",
                        "maxLength": 8000,
                        "nullable": true
                      },
                      "blocks": {
                        "type": "array",
                        "items": {
                          "anyOf": [
                            {
                              "type": "object",
                              "properties": {
                                "kind": {
                                  "type": "string",
                                  "enum": [
                                    "text"
                                  ]
                                },
                                "title": {
                                  "type": "string"
                                },
                                "text": {
                                  "type": "string",
                                  "maxLength": 8000
                                }
                              },
                              "required": [
                                "kind",
                                "text"
                              ],
                              "additionalProperties": false
                            },
                            {
                              "type": "object",
                              "properties": {
                                "kind": {
                                  "type": "string",
                                  "enum": [
                                    "fields"
                                  ]
                                },
                                "title": {
                                  "type": "string"
                                },
                                "fields": {
                                  "type": "array",
                                  "items": {
                                    "type": "object",
                                    "properties": {
                                      "label": {
                                        "type": "string"
                                      },
                                      "value": {
                                        "type": "string",
                                        "maxLength": 2000
                                      }
                                    },
                                    "required": [
                                      "label",
                                      "value"
                                    ],
                                    "additionalProperties": false
                                  }
                                }
                              },
                              "required": [
                                "kind",
                                "fields"
                              ],
                              "additionalProperties": false
                            },
                            {
                              "type": "object",
                              "properties": {
                                "kind": {
                                  "type": "string",
                                  "enum": [
                                    "tool_call"
                                  ]
                                },
                                "title": {
                                  "type": "string"
                                },
                                "tool": {
                                  "type": "string"
                                },
                                "args": {
                                  "type": "object",
                                  "additionalProperties": {}
                                }
                              },
                              "required": [
                                "kind",
                                "tool",
                                "args"
                              ],
                              "additionalProperties": false
                            },
                            {
                              "type": "object",
                              "properties": {
                                "kind": {
                                  "type": "string",
                                  "enum": [
                                    "diff"
                                  ]
                                },
                                "title": {
                                  "type": "string"
                                },
                                "before": {
                                  "type": "string",
                                  "maxLength": 8000
                                },
                                "after": {
                                  "type": "string",
                                  "maxLength": 8000
                                }
                              },
                              "required": [
                                "kind",
                                "before",
                                "after"
                              ],
                              "additionalProperties": false
                            },
                            {
                              "type": "object",
                              "properties": {
                                "kind": {
                                  "type": "string",
                                  "enum": [
                                    "ai_output"
                                  ]
                                },
                                "title": {
                                  "type": "string"
                                },
                                "model": {
                                  "type": "string"
                                },
                                "prompt": {
                                  "type": "string",
                                  "maxLength": 8000
                                },
                                "output": {
                                  "type": "string",
                                  "maxLength": 8000
                                },
                                "costUsd": {
                                  "type": "number"
                                },
                                "tokens": {
                                  "type": "number"
                                }
                              },
                              "required": [
                                "kind",
                                "output"
                              ],
                              "additionalProperties": false
                            }
                          ]
                        }
                      },
                      "payload": {
                        "type": "object",
                        "additionalProperties": {},
                        "nullable": true
                      },
                      "sourceUrl": {
                        "type": "string",
                        "nullable": true
                      },
                      "extra": {
                        "type": "object",
                        "additionalProperties": {}
                      }
                    },
                    "additionalProperties": false
                  },
                  "proposedOutput": {
                    "type": "object",
                    "properties": {
                      "title": {
                        "type": "string",
                        "maxLength": 200
                      },
                      "format": {
                        "type": "string",
                        "enum": [
                          "text",
                          "markdown"
                        ]
                      },
                      "content": {
                        "type": "string",
                        "maxLength": 50000
                      }
                    },
                    "required": [
                      "content"
                    ],
                    "additionalProperties": false
                  },
                  "responseSpec": {
                    "anyOf": [
                      {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "approval"
                            ]
                          }
                        },
                        "required": [
                          "type"
                        ],
                        "additionalProperties": false
                      },
                      {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "select"
                            ]
                          },
                          "options": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "id": {
                                  "type": "string",
                                  "minLength": 1,
                                  "maxLength": 100
                                },
                                "label": {
                                  "type": "string",
                                  "minLength": 1,
                                  "maxLength": 200
                                },
                                "description": {
                                  "type": "string",
                                  "maxLength": 500
                                }
                              },
                              "required": [
                                "id",
                                "label"
                              ],
                              "additionalProperties": false
                            },
                            "minItems": 2,
                            "maxItems": 20
                          },
                          "min": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "max": {
                            "type": "integer",
                            "exclusiveMinimum": true,
                            "minimum": 0
                          }
                        },
                        "required": [
                          "type",
                          "options"
                        ],
                        "additionalProperties": false
                      },
                      {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "text"
                            ]
                          },
                          "multiline": {
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "type"
                        ],
                        "additionalProperties": false
                      }
                    ]
                  },
                  "timeoutSec": {
                    "type": "integer",
                    "exclusiveMinimum": true,
                    "minimum": 0
                  }
                },
                "required": [
                  "externalRunId",
                  "stepRef"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approvalNeeded": {
                      "type": "boolean"
                    },
                    "decisionId": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "approvalNeeded"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "201": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approvalNeeded": {
                      "type": "boolean"
                    },
                    "decisionId": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "approvalNeeded"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "used": {
                      "type": "number"
                    },
                    "limit": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "error",
                    "message"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "402": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "used": {
                      "type": "number"
                    },
                    "limit": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "error",
                    "message"
                  ],
                  "additionalProperties": false
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "List decisions (API-key scope) — the agent's inbox",
        "tags": [
          "Worker"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "PENDING",
                "APPROVED",
                "REJECTED",
                "EXPIRED",
                "CANCELLED"
              ]
            },
            "in": "query",
            "name": "status",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": true,
              "minimum": 0,
              "maximum": 200
            },
            "in": "query",
            "name": "limit",
            "required": false
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/worker/decisions/{id}": {
      "get": {
        "summary": "Get a decision status (API-key scope) — poll a park-and-resume verdict",
        "tags": [
          "Worker"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/worker/decisions/{id}/cancel": {
      "post": {
        "summary": "Cancel a pending decision (API-key scope)",
        "tags": [
          "Worker"
        ],
        "description": "Withdraw a decision the agent no longer needs. Only PENDING decisions cancel; an already-resolved one returns cancelled=false with its current status.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "anyOf": [
                  {
                    "not": {}
                  },
                  {
                    "type": "object",
                    "properties": {
                      "note": {
                        "type": "string"
                      }
                    },
                    "additionalProperties": false,
                    "nullable": true
                  }
                ]
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/worker/policies": {
      "post": {
        "summary": "Upsert a named approval policy",
        "tags": [
          "Worker"
        ],
        "description": "Declare an org-scoped approval policy: a mandatory floor (when a human is required) plus defaults for assignee, approver group, and quorum. Idempotent upsert keyed by (org, name).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1
                  },
                  "floor": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "field": {
                          "type": "string",
                          "minLength": 1
                        },
                        "op": {
                          "type": "string",
                          "enum": [
                            "gt",
                            "gte",
                            "lt",
                            "lte",
                            "eq",
                            "ne"
                          ]
                        },
                        "value": {
                          "anyOf": [
                            {
                              "type": "number"
                            },
                            {
                              "type": "string"
                            },
                            {
                              "type": "boolean"
                            }
                          ]
                        }
                      },
                      "required": [
                        "field",
                        "op",
                        "value"
                      ],
                      "additionalProperties": false
                    },
                    "default": []
                  },
                  "defaults": {
                    "type": "object",
                    "properties": {
                      "assigneeEmail": {
                        "type": "string",
                        "format": "email"
                      },
                      "route": {
                        "type": "object",
                        "properties": {
                          "channels": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "enum": [
                                "email",
                                "slack",
                                "discord",
                                "phone",
                                "sms",
                                "webhook"
                              ]
                            },
                            "minItems": 1
                          },
                          "strategy": {
                            "type": "string",
                            "enum": [
                              "fanout",
                              "escalate"
                            ]
                          },
                          "escalateAfterSec": {
                            "type": "integer",
                            "exclusiveMinimum": true,
                            "minimum": 0
                          }
                        },
                        "required": [
                          "channels"
                        ],
                        "additionalProperties": false
                      },
                      "surface": {
                        "type": "string",
                        "enum": [
                          "inbox",
                          "slack",
                          "discord",
                          "docusign",
                          "email",
                          "api"
                        ]
                      },
                      "approvers": {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "format": "email"
                        }
                      },
                      "approvalsRequired": {
                        "type": "integer",
                        "exclusiveMinimum": true,
                        "minimum": 0
                      },
                      "rejectionsRequired": {
                        "type": "integer",
                        "exclusiveMinimum": true,
                        "minimum": 0
                      }
                    },
                    "additionalProperties": false,
                    "default": {}
                  }
                },
                "required": [
                  "name"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/runs/": {
      "post": {
        "summary": "Trigger a workflow run",
        "tags": [
          "Runs"
        ],
        "description": "Fire the external engine event for a registered workflow. Flowplane seeds no run/step rows — the engine owns execution; Flowplane tracks the decisions requested on the run.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "workflowId": {
                    "type": "string",
                    "minLength": 1
                  },
                  "input": {
                    "type": "object",
                    "additionalProperties": {}
                  },
                  "idempotencyKey": {
                    "type": "string"
                  },
                  "timeoutSec": {
                    "type": "integer",
                    "exclusiveMinimum": true,
                    "minimum": 0
                  }
                },
                "required": [
                  "workflowId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/workflows/": {
      "get": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/workflows/{id}": {
      "get": {
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/api-keys/": {
      "post": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "get": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/api-keys/{id}": {
      "delete": {
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/api-keys/{id}/rotate": {
      "post": {
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/audit/": {
      "get": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/settings/retention": {
      "get": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "patch": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/settings/llm": {
      "get": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "put": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/integrations/discord": {
      "get": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/integrations/discord/channels": {
      "get": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/integrations/discord/channel": {
      "put": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/integrations/discord/test": {
      "post": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/integrations/slack": {
      "get": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/integrations/slack/channels": {
      "get": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/integrations/slack/channel": {
      "put": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/integrations/slack/test": {
      "post": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/usage/": {
      "get": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/usage/current": {
      "get": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/approvers/": {
      "post": {
        "summary": "Register an approver",
        "tags": [
          "Approvers"
        ],
        "description": "Admin-initiated registration. Creates a person with declared channels/methods; email/inbox are auto-verified, the rest land pending until the approver self-verifies. Idempotent per (org, email).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1
                  },
                  "clerkUserId": {
                    "type": "string"
                  },
                  "channels": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "channel": {
                          "type": "string",
                          "enum": [
                            "email",
                            "slack",
                            "discord",
                            "phone",
                            "sms",
                            "webhook"
                          ]
                        },
                        "endpoint": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "channel"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "methods": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "surface": {
                          "type": "string",
                          "enum": [
                            "inbox",
                            "slack",
                            "discord",
                            "docusign",
                            "email",
                            "api"
                          ]
                        },
                        "resolvedRef": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "surface"
                      ],
                      "additionalProperties": false
                    }
                  }
                },
                "required": [
                  "email"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "get": {
        "summary": "List approvers",
        "tags": [
          "Approvers"
        ],
        "description": "All approver identities for the org, with channel + method verification state.",
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/approvers/{id}": {
      "get": {
        "summary": "Get an approver",
        "tags": [
          "Approvers"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/approvers/{id}/verify": {
      "post": {
        "summary": "Verify an approver capability",
        "tags": [
          "Approvers"
        ],
        "description": "Self-serve completion: mark a declared channel or surface verified (or failed), persisting the proven endpoint/ref. Real per-channel proof (OTP, Slack lookup, DocuSign mapping) plugs in here.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "channel": {
                    "type": "string",
                    "enum": [
                      "email",
                      "slack",
                      "discord",
                      "phone",
                      "sms",
                      "webhook"
                    ]
                  },
                  "surface": {
                    "type": "string",
                    "enum": [
                      "inbox",
                      "slack",
                      "discord",
                      "docusign",
                      "email",
                      "api"
                    ]
                  },
                  "endpoint": {
                    "type": "string"
                  },
                  "resolvedRef": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "verified",
                      "failed"
                    ]
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/approvers/{id}/disable": {
      "post": {
        "summary": "Disable an approver",
        "tags": [
          "Approvers"
        ],
        "description": "Soft-disable. The approver stops binding any surface and is never routed a decision. Policies naming them still resolve, but report as unsatisfiable — returned in `warnings`. Reversible via /enable. Idempotent.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/approvers/{id}/enable": {
      "post": {
        "summary": "Re-enable a disabled approver",
        "tags": [
          "Approvers"
        ],
        "description": "Clears the disable. Registration deliberately does not do this — bringing someone back is an explicit act. Idempotent.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/policies/": {
      "get": {
        "summary": "List approval policies",
        "tags": [
          "Policies"
        ],
        "description": "All named approval policies for the org.",
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Create (or upsert) an approval policy",
        "tags": [
          "Policies"
        ],
        "description": "Create a named org-scoped approval policy: a mandatory floor plus defaults for surface, route, approver group, and quorum. Idempotent upsert keyed by (org, name). Rejects unsatisfiable policies with 422.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1
                  },
                  "floor": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "field": {
                          "type": "string",
                          "minLength": 1
                        },
                        "op": {
                          "type": "string",
                          "enum": [
                            "gt",
                            "gte",
                            "lt",
                            "lte",
                            "eq",
                            "ne"
                          ]
                        },
                        "value": {
                          "anyOf": [
                            {
                              "type": "number"
                            },
                            {
                              "type": "string"
                            },
                            {
                              "type": "boolean"
                            }
                          ]
                        }
                      },
                      "required": [
                        "field",
                        "op",
                        "value"
                      ],
                      "additionalProperties": false
                    },
                    "default": []
                  },
                  "defaults": {
                    "type": "object",
                    "properties": {
                      "assigneeEmail": {
                        "type": "string",
                        "format": "email"
                      },
                      "route": {
                        "type": "object",
                        "properties": {
                          "channels": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "enum": [
                                "email",
                                "slack",
                                "discord",
                                "phone",
                                "sms",
                                "webhook"
                              ]
                            },
                            "minItems": 1
                          },
                          "strategy": {
                            "type": "string",
                            "enum": [
                              "fanout",
                              "escalate"
                            ]
                          },
                          "escalateAfterSec": {
                            "type": "integer",
                            "exclusiveMinimum": true,
                            "minimum": 0
                          }
                        },
                        "required": [
                          "channels"
                        ],
                        "additionalProperties": false
                      },
                      "surface": {
                        "type": "string",
                        "enum": [
                          "inbox",
                          "slack",
                          "discord",
                          "docusign",
                          "email",
                          "api"
                        ]
                      },
                      "approvers": {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "format": "email"
                        }
                      },
                      "approvalsRequired": {
                        "type": "integer",
                        "exclusiveMinimum": true,
                        "minimum": 0
                      },
                      "rejectionsRequired": {
                        "type": "integer",
                        "exclusiveMinimum": true,
                        "minimum": 0
                      }
                    },
                    "additionalProperties": false,
                    "default": {}
                  }
                },
                "required": [
                  "name"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/policies/{name}": {
      "get": {
        "summary": "Get an approval policy",
        "tags": [
          "Policies"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "in": "path",
            "name": "name",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "put": {
        "summary": "Update an approval policy",
        "tags": [
          "Policies"
        ],
        "description": "Replace an existing policy’s floor + defaults. 404 if it does not exist. Rejects unsatisfiable policies with 422.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "floor": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "field": {
                          "type": "string",
                          "minLength": 1
                        },
                        "op": {
                          "type": "string",
                          "enum": [
                            "gt",
                            "gte",
                            "lt",
                            "lte",
                            "eq",
                            "ne"
                          ]
                        },
                        "value": {
                          "anyOf": [
                            {
                              "type": "number"
                            },
                            {
                              "type": "string"
                            },
                            {
                              "type": "boolean"
                            }
                          ]
                        }
                      },
                      "required": [
                        "field",
                        "op",
                        "value"
                      ],
                      "additionalProperties": false
                    },
                    "default": []
                  },
                  "defaults": {
                    "type": "object",
                    "properties": {
                      "assigneeEmail": {
                        "type": "string",
                        "format": "email"
                      },
                      "route": {
                        "type": "object",
                        "properties": {
                          "channels": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "enum": [
                                "email",
                                "slack",
                                "discord",
                                "phone",
                                "sms",
                                "webhook"
                              ]
                            },
                            "minItems": 1
                          },
                          "strategy": {
                            "type": "string",
                            "enum": [
                              "fanout",
                              "escalate"
                            ]
                          },
                          "escalateAfterSec": {
                            "type": "integer",
                            "exclusiveMinimum": true,
                            "minimum": 0
                          }
                        },
                        "required": [
                          "channels"
                        ],
                        "additionalProperties": false
                      },
                      "surface": {
                        "type": "string",
                        "enum": [
                          "inbox",
                          "slack",
                          "discord",
                          "docusign",
                          "email",
                          "api"
                        ]
                      },
                      "approvers": {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "format": "email"
                        }
                      },
                      "approvalsRequired": {
                        "type": "integer",
                        "exclusiveMinimum": true,
                        "minimum": 0
                      },
                      "rejectionsRequired": {
                        "type": "integer",
                        "exclusiveMinimum": true,
                        "minimum": 0
                      }
                    },
                    "additionalProperties": false,
                    "default": {}
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "in": "path",
            "name": "name",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "delete": {
        "summary": "Delete an approval policy",
        "tags": [
          "Policies"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "in": "path",
            "name": "name",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/notification-rules/": {
      "get": {
        "summary": "Get my notification rules (ladder)",
        "tags": [
          "Notifications"
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "put": {
        "summary": "Replace my notification rules",
        "tags": [
          "Notifications"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "rules": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "afterSeconds": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "channel": {
                          "type": "string",
                          "enum": [
                            "email",
                            "slack",
                            "phone",
                            "sms",
                            "webhook"
                          ]
                        }
                      },
                      "required": [
                        "afterSeconds",
                        "channel"
                      ],
                      "additionalProperties": false
                    }
                  }
                },
                "required": [
                  "rules"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/contact-methods/": {
      "get": {
        "summary": "Get my contact methods",
        "tags": [
          "Notifications"
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "put": {
        "summary": "Update my contact methods",
        "tags": [
          "Notifications"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "nullable": true
                  },
                  "sms": {
                    "type": "string",
                    "nullable": true
                  },
                  "slack": {
                    "type": "string",
                    "nullable": true
                  },
                  "pushEnabled": {
                    "type": "boolean"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/push-devices/": {
      "post": {
        "summary": "Register this device for push",
        "tags": [
          "Notifications"
        ],
        "description": "Idempotent on (user, token) — safe to call on every launch, which the client should do because push tokens rotate on reinstall and restore. Re-registering a revoked device reactivates it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255
                  },
                  "platform": {
                    "type": "string",
                    "enum": [
                      "ios",
                      "android"
                    ]
                  },
                  "deviceName": {
                    "type": "string",
                    "maxLength": 120,
                    "nullable": true
                  }
                },
                "required": [
                  "token",
                  "platform"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "get": {
        "summary": "List my registered devices",
        "tags": [
          "Notifications"
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/push-devices/{id}": {
      "delete": {
        "summary": "Revoke one of my devices",
        "tags": [
          "Notifications"
        ],
        "description": "Soft-revoke. Call on sign-out, otherwise the next person to sign in on that handset keeps receiving the previous user’s notifications.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/directory/actor": {
      "get": {
        "summary": "Resolve an internal actor id to a display label",
        "tags": [
          "Directory"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "in": "query",
            "name": "ref",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/billing/": {
      "get": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/billing/checkout": {
      "post": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/billing/portal": {
      "post": {
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/decisions/": {
      "get": {
        "summary": "List decisions (reviewer inbox)",
        "tags": [
          "Decisions"
        ],
        "description": "List decisions for the org, newest first. Filter by status and assignee email for the reviewer inbox.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "PENDING",
                "APPROVED",
                "REJECTED",
                "EXPIRED",
                "CANCELLED"
              ]
            },
            "in": "query",
            "name": "status",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "format": "email"
            },
            "in": "query",
            "name": "assignee",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/decisions/{id}": {
      "get": {
        "summary": "Get a decision with its quorum tally",
        "tags": [
          "Decisions"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/decisions/{id}/output": {
      "put": {
        "summary": "Save the reviewer’s refined draft (conversational refinement)",
        "tags": [
          "Decisions"
        ],
        "description": "Persist an edited version of a refinable decision’s proposed output. On approve, the engine resumes with the refined draft (refinedOutput ?? proposedOutput). Only allowed while the decision is PENDING and carries a proposedOutput.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "text",
                      "markdown"
                    ]
                  },
                  "content": {
                    "type": "string",
                    "maxLength": 50000
                  }
                },
                "required": [
                  "content"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/decisions/{id}/refine": {
      "post": {
        "summary": "Refine the draft with the org’s own model (BYO)",
        "tags": [
          "Decisions"
        ],
        "description": "Apply the reviewer’s feedback to the current draft using the org’s configured LLM (Settings → Refine with AI). Returns the revised draft and appends the turn to the refinement log. Does not commit — the reviewer saves via PUT /output, then approves.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "feedback": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 4000
                  }
                },
                "required": [
                  "feedback"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/v1/decisions/{id}/decide": {
      "post": {
        "summary": "Approve or reject a decision",
        "tags": [
          "Decisions"
        ],
        "description": "Record a vote. Under quorum the decision resolves only when the approval/rejection threshold is met; reject-wins by default. On resolution Flowplane fires the engine resume event.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "decision": {
                    "type": "string",
                    "enum": [
                      "approve",
                      "reject"
                    ]
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 1000
                  },
                  "responseValue": {
                    "anyOf": [
                      {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "approval"
                            ]
                          },
                          "decision": {
                            "type": "string",
                            "enum": [
                              "approve",
                              "reject"
                            ]
                          }
                        },
                        "required": [
                          "type",
                          "decision"
                        ],
                        "additionalProperties": false
                      },
                      {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "select"
                            ]
                          },
                          "selected": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "maxItems": 20
                          }
                        },
                        "required": [
                          "type",
                          "selected"
                        ],
                        "additionalProperties": false
                      },
                      {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "text"
                            ]
                          },
                          "text": {
                            "type": "string",
                            "maxLength": 50000
                          }
                        },
                        "required": [
                          "type",
                          "text"
                        ],
                        "additionalProperties": false
                      }
                    ]
                  }
                },
                "required": [
                  "decision"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    }
  },
  "servers": [
    {
      "url": "http://localhost:3001"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ]
}
