{
  "openapi": "3.1.0",
  "info": {
    "title": "FastFollow API",
    "version": "1.0.0",
    "description": "The FastFollow API powers automated sales follow-ups, CRM synchronization, meeting ingestion, proposal management, and AI-driven coaching. All endpoints are tenant-scoped and require authentication.",
    "contact": {
      "name": "FastFollow Developer Support",
      "email": "developers@fastfollow.ai",
      "url": "https://fastfollow.ai/docs"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://fastfollow.ai/terms"
    }
  },
  "servers": [
    {
      "url": "https://api.fastfollow.ai/api",
      "description": "Production"
    },
    {
      "url": "http://localhost:3001/api",
      "description": "Local development"
    }
  ],
  "security": [{ "bearerAuth": [] }, { "sessionCookie": [] }],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Pass your API token in the Authorization header: `Authorization: Bearer <token>`"
      },
      "sessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "next-auth.session-token",
        "description": "NextAuth.js session cookie set automatically after sign-in."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": { "type": "string", "description": "Human-readable error code" },
          "message": { "type": "string", "description": "Detailed error message" },
          "details": {
            "description": "Structured validation errors when present"
          }
        }
      },
      "Success": {
        "type": "object",
        "required": ["success"],
        "properties": {
          "success": { "type": "boolean", "example": true }
        }
      },
      "CrmContact": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "description": "CRM-native contact ID" },
          "email": { "type": "string", "format": "email" },
          "firstName": { "type": "string" },
          "lastName": { "type": "string" },
          "company": { "type": "string" },
          "phone": { "type": "string" },
          "customFields": {
            "type": "object",
            "additionalProperties": true,
            "description": "Provider-specific extra fields (HubSpot / Salesforce)"
          }
        }
      },
      "SyncResult": {
        "type": "object",
        "properties": {
          "synced": { "type": "integer" },
          "failed": { "type": "integer" },
          "errors": {
            "type": "array",
            "items": { "type": "string" }
          }
        }
      },
      "Meeting": {
        "type": "object",
        "properties": {
          "meetingId": { "type": "string", "format": "uuid" },
          "title": { "type": "string" },
          "startTime": { "type": "string", "format": "date-time" },
          "endTime": { "type": "string", "format": "date-time" },
          "platform": { "type": "string", "example": "google_meet" },
          "participants": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": { "type": "string" },
                "email": { "type": "string", "format": "email" },
                "role": { "type": "string" }
              }
            }
          },
          "transcriptStatus": {
            "type": "string",
            "enum": ["pending", "processing", "completed", "unavailable"]
          }
        }
      },
      "Transcript": {
        "type": "object",
        "properties": {
          "transcriptId": { "type": "string", "format": "uuid" },
          "summary": { "type": "string" },
          "actionItems": { "type": "array", "items": { "type": "string" } },
          "keyTopics": { "type": "array", "items": { "type": "string" } },
          "sentimentScores": {
            "type": "object",
            "additionalProperties": { "type": "number" }
          },
          "speakerSegments": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "speaker": { "type": "string" },
                "text": { "type": "string" },
                "timestamp": { "type": "string" }
              }
            }
          },
          "structuredTranscript": { "type": "string" },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "ProposalSection": {
        "type": "object",
        "required": ["title", "content", "order"],
        "properties": {
          "title": { "type": "string", "maxLength": 500 },
          "content": { "type": "string", "maxLength": 50000 },
          "order": { "type": "integer", "minimum": 0 }
        }
      },
      "PricingLineItem": {
        "type": "object",
        "required": ["name", "quantity", "unitPrice", "total"],
        "properties": {
          "name": { "type": "string", "maxLength": 255 },
          "quantity": { "type": "number", "minimum": 0 },
          "unitPrice": { "type": "number", "minimum": 0 },
          "total": { "type": "number", "minimum": 0 }
        }
      },
      "Pricing": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/PricingLineItem" }
          },
          "subtotal": { "type": "number", "minimum": 0 },
          "tax": { "type": "number", "minimum": 0 },
          "total": { "type": "number", "minimum": 0 },
          "currency": { "type": "string", "pattern": "^[A-Z]{3}$", "example": "USD" }
        }
      },
      "Proposal": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "title": { "type": "string" },
          "clientName": { "type": "string" },
          "status": {
            "type": "string",
            "enum": ["draft", "review", "approved", "sent", "accepted", "rejected"]
          },
          "templateType": { "type": "string", "example": "custom" },
          "sections": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ProposalSection" }
          },
          "pricing": { "$ref": "#/components/schemas/Pricing" },
          "validUntil": { "type": "string", "format": "date-time", "nullable": true },
          "crmDealId": { "type": "string", "nullable": true },
          "metadata": { "type": "object", "additionalProperties": true },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "UpcomingMeeting": {
        "type": "object",
        "properties": {
          "eventId": { "type": "string" },
          "title": { "type": "string" },
          "startTime": { "type": "string", "format": "date-time" },
          "endTime": { "type": "string", "format": "date-time" },
          "participantCount": { "type": "integer" },
          "participants": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": { "type": "string" },
                "email": { "type": "string", "format": "email" },
                "responseStatus": { "type": "string" }
              }
            }
          },
          "meetLink": { "type": "string", "nullable": true },
          "calendarLink": { "type": "string", "nullable": true },
          "description": { "type": "string", "nullable": true },
          "isGoogleMeet": { "type": "boolean" }
        }
      },
      "CoachingSession": {
        "type": "object",
        "properties": {
          "sessionId": { "type": "string" },
          "meetingId": { "type": "string", "format": "uuid" },
          "meetingTitle": { "type": "string" },
          "userId": { "type": "string" },
          "tenantId": { "type": "string" },
          "objections": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "title": { "type": "string" },
                "transcript": { "type": "string" },
                "type": {
                  "type": "string",
                  "enum": ["price", "timing", "competition", "authority", "feature", "other"]
                },
                "severity": {
                  "type": "string",
                  "enum": ["low", "medium", "high", "critical"]
                },
                "speaker": { "type": "string" },
                "suggestedResponses": {
                  "type": "array",
                  "items": { "type": "string" }
                },
                "framework": { "type": "string" }
              }
            }
          },
          "participants": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": { "type": "string" },
                "role": { "type": "string" },
                "company": { "type": "string" },
                "influence": {
                  "type": "string",
                  "enum": ["low", "medium", "high"]
                }
              }
            }
          },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "OnboardingState": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": ["not_started", "in_progress", "completed"]
          },
          "progress": { "type": "number", "minimum": 0, "maximum": 100 },
          "completedAt": { "type": "string", "format": "date-time", "nullable": true },
          "completedSteps": {
            "type": "array",
            "items": { "type": "integer" }
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "step": { "type": "integer" },
                "title": { "type": "string" },
                "description": { "type": "string" },
                "completed": { "type": "boolean" }
              }
            }
          }
        }
      },
      "AnalyticsResponse": {
        "type": "object",
        "properties": {
          "meta": {
            "type": "object",
            "properties": {
              "tenantId": { "type": "string" },
              "timeRange": { "type": "string" },
              "startDate": { "type": "string", "format": "date-time" },
              "endDate": { "type": "string", "format": "date-time" },
              "generatedAt": { "type": "string", "format": "date-time" }
            }
          },
          "summary": {
            "type": "object",
            "properties": {
              "totalFollowUpsSent": { "type": "integer" },
              "responseRate": { "type": "number" },
              "meetingsProcessed": { "type": "integer" },
              "avgTimeToSendMinutes": { "type": "integer" },
              "workflowCompletionRate": { "type": "number" }
            }
          },
          "followUpsTrend": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": { "type": "string", "format": "date" },
                "sent": { "type": "integer" },
                "delivered": { "type": "integer" },
                "opened": { "type": "integer" }
              }
            }
          },
          "emailStatusBreakdown": {
            "type": "object",
            "properties": {
              "queued": { "type": "integer" },
              "sent": { "type": "integer" },
              "delivered": { "type": "integer" },
              "bounced": { "type": "integer" },
              "failed": { "type": "integer" },
              "delivery_delayed": { "type": "integer" }
            }
          },
          "topTemplates": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "templateType": { "type": "string" },
                "totalSent": { "type": "integer" },
                "delivered": { "type": "integer" },
                "deliveryRate": { "type": "number" }
              }
            }
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Authentication is required or the token is invalid.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "Unauthorized" }
          }
        }
      },
      "BadRequest": {
        "description": "The request body or query parameters failed validation.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": "Invalid request body",
              "details": { "fieldErrors": { "to": ["At least one recipient is required"] } }
            }
          }
        }
      },
      "NotFound": {
        "description": "The requested resource was not found.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "Not Found", "message": "Resource not found" }
          }
        }
      },
      "InternalServerError": {
        "description": "An unexpected server error occurred.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "Internal Server Error", "message": "An unexpected error occurred" }
          }
        }
      }
    }
  },
  "tags": [
    { "name": "CRM", "description": "Connect and sync contacts and deals from HubSpot and Salesforce" },
    { "name": "Meetings", "description": "Ingest Google Meet meetings and retrieve AI-processed transcripts" },
    { "name": "Email", "description": "Send automated follow-up emails via Resend" },
    { "name": "Proposals", "description": "Create, manage, and export sales proposals" },
    { "name": "Reports", "description": "Analytics and performance metrics for your team" },
    { "name": "Calendar", "description": "Upcoming Google Calendar events enriched with Meet links" },
    { "name": "Objection Coaching", "description": "AI-powered objection handling from meeting transcripts" },
    { "name": "Onboarding", "description": "User onboarding state and progress tracking" },
    { "name": "Follow-ups", "description": "Review and approve AI-generated follow-up items" },
    { "name": "Demo", "description": "Sandbox demo environment for prospect evaluation" }
  ],
  "paths": {
    "/crm/sync": {
      "post": {
        "tags": ["CRM"],
        "operationId": "triggerCrmSync",
        "summary": "Trigger CRM sync",
        "description": "Triggers a bidirectional sync between FastFollow and the tenant's connected CRM (HubSpot or Salesforce). Contacts and deals are synced concurrently. Defaults to syncing both object types when `syncTypes` is omitted.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "syncTypes": {
                    "type": "array",
                    "items": { "type": "string", "enum": ["contact", "deal"] },
                    "default": ["contact", "deal"],
                    "description": "Which CRM object types to sync. Omit to sync all."
                  }
                }
              },
              "example": { "syncTypes": ["contact", "deal"] }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sync completed successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Success" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "connectionId": { "type": "string" },
                            "provider": { "type": "string", "enum": ["hubspot", "salesforce"] },
                            "syncedAt": { "type": "string", "format": "date-time" },
                            "syncTypes": { "type": "array", "items": { "type": "string" } },
                            "results": {
                              "type": "object",
                              "additionalProperties": { "$ref": "#/components/schemas/SyncResult" }
                            },
                            "summary": {
                              "type": "object",
                              "properties": {
                                "totalSynced": { "type": "integer" },
                                "totalFailed": { "type": "integer" }
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "connectionId": "conn_01HXYZ",
                    "provider": "hubspot",
                    "syncedAt": "2026-03-19T10:00:00.000Z",
                    "syncTypes": ["contact", "deal"],
                    "results": {
                      "contact": { "synced": 47, "failed": 0, "errors": [] },
                      "deal": { "synced": 12, "failed": 0, "errors": [] }
                    },
                    "summary": { "totalSynced": 59, "totalFailed": 0 }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": {
            "description": "No active CRM connection found for this tenant.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": {
                  "error": "No active CRM connection found",
                  "hint": "Connect a HubSpot or Salesforce account via /api/connections"
                }
              }
            }
          },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/crm/contacts": {
      "get": {
        "tags": ["CRM"],
        "operationId": "searchCrmContacts",
        "summary": "Search CRM contacts",
        "description": "Search contacts in the connected CRM by free-text query or exact email match. At least one of `q` or `email` must be provided. Results are scoped to the authenticated tenant's CRM connection.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Free-text search query passed to the CRM provider.",
            "schema": { "type": "string" },
            "example": "Acme Corp"
          },
          {
            "name": "email",
            "in": "query",
            "description": "Exact email address lookup. Takes precedence over `q` when both are supplied.",
            "schema": { "type": "string", "format": "email" },
            "example": "jane@acme.com"
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of contacts to return (1–50).",
            "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 20 }
          }
        ],
        "responses": {
          "200": {
            "description": "Contact search results.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Success" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "contacts": {
                              "type": "array",
                              "items": { "$ref": "#/components/schemas/CrmContact" }
                            },
                            "count": { "type": "integer" },
                            "provider": { "type": "string", "enum": ["hubspot", "salesforce"] }
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "contacts": [
                      {
                        "id": "hs-001",
                        "email": "jane@acme.com",
                        "firstName": "Jane",
                        "lastName": "Smith",
                        "company": "Acme Corp"
                      }
                    ],
                    "count": 1,
                    "provider": "hubspot"
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/meetings/ingest": {
      "post": {
        "tags": ["Meetings"],
        "operationId": "ingestMeeting",
        "summary": "Ingest meeting data",
        "description": "Ingest a specific Google Calendar event or sync all recent Google Meet meetings (last 7 days). Requires an active Google OAuth connection with calendar and Meet scopes. Optionally include a raw transcript for immediate processing.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "required": ["eventId"],
                    "properties": {
                      "eventId": {
                        "type": "string",
                        "description": "Google Calendar event ID to ingest."
                      },
                      "rawTranscript": {
                        "type": "string",
                        "description": "Optional raw transcript text to associate with the meeting."
                      }
                    }
                  },
                  {
                    "type": "object",
                    "required": ["syncAll"],
                    "properties": {
                      "syncAll": {
                        "type": "boolean",
                        "enum": [true],
                        "description": "Set to true to sync all recent Google Meet events from the past 7 days."
                      }
                    }
                  }
                ]
              },
              "examples": {
                "singleEvent": {
                  "summary": "Ingest a specific event",
                  "value": { "eventId": "abc123_google_event_id" }
                },
                "syncAll": {
                  "summary": "Sync all recent meetings",
                  "value": { "syncAll": true }
                },
                "withTranscript": {
                  "summary": "Ingest with raw transcript",
                  "value": {
                    "eventId": "abc123_google_event_id",
                    "rawTranscript": "[0:00] Jane Smith: Thanks for joining today's call..."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Meeting updated (already existed).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Success" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "meeting": { "$ref": "#/components/schemas/Meeting" },
                            "transcript": { "$ref": "#/components/schemas/Transcript" },
                            "isNew": { "type": "boolean" }
                          }
                        },
                        "message": { "type": "string" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "201": {
            "description": "Meeting ingested successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Success" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "meeting": { "$ref": "#/components/schemas/Meeting" },
                            "transcript": { "$ref": "#/components/schemas/Transcript" },
                            "isNew": { "type": "boolean", "example": true }
                          }
                        },
                        "message": { "type": "string", "example": "Meeting ingested successfully" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/meetings/{id}/transcript": {
      "get": {
        "tags": ["Meetings"],
        "operationId": "getMeetingTranscript",
        "summary": "Get meeting transcript",
        "description": "Returns the AI-processed transcript for a specific ingested meeting, including summary, action items, key topics, sentiment scores, and speaker segments. Returns 404 with transcript status if processing is still pending.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The UUID of the ingested meeting.",
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "200": {
            "description": "Transcript retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Success" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "meeting": { "$ref": "#/components/schemas/Meeting" },
                            "transcript": { "$ref": "#/components/schemas/Transcript" }
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "meeting": {
                      "meetingId": "550e8400-e29b-41d4-a716-446655440000",
                      "title": "Q1 Renewal — Acme Corp",
                      "startTime": "2026-03-15T14:00:00.000Z",
                      "endTime": "2026-03-15T14:45:00.000Z",
                      "platform": "google_meet",
                      "transcriptStatus": "completed"
                    },
                    "transcript": {
                      "transcriptId": "tr_01ABC",
                      "summary": "Discussed Q1 renewal pricing and feature gaps. Client interested in Enterprise plan.",
                      "actionItems": ["Send updated pricing deck", "Schedule exec review call"],
                      "keyTopics": ["pricing", "enterprise features", "timeline"]
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/email/send": {
      "post": {
        "tags": ["Email"],
        "operationId": "sendEmail",
        "summary": "Send follow-up email",
        "description": "Sends a follow-up email via Resend and records the delivery attempt. Supports up to 50 recipients and 20 CC addresses. A delivery record is created before sending so you can track status via webhooks.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["to", "subject", "htmlBody"],
                "properties": {
                  "followUpId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Optional follow-up item UUID to associate this delivery with."
                  },
                  "to": {
                    "type": "array",
                    "items": { "type": "string", "format": "email" },
                    "minItems": 1,
                    "maxItems": 50,
                    "description": "Recipient email addresses."
                  },
                  "cc": {
                    "type": "array",
                    "items": { "type": "string", "format": "email" },
                    "maxItems": 20,
                    "description": "CC email addresses."
                  },
                  "subject": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 500
                  },
                  "htmlBody": {
                    "type": "string",
                    "description": "HTML email body. Must be a complete HTML fragment."
                  },
                  "textBody": {
                    "type": "string",
                    "description": "Plain text fallback body."
                  },
                  "replyTo": {
                    "type": "string",
                    "format": "email",
                    "description": "Reply-to email address."
                  },
                  "templateType": {
                    "type": "string",
                    "enum": ["followUp", "meetingSummary", "noShowRecovery"],
                    "description": "Template identifier for analytics segmentation."
                  }
                }
              },
              "example": {
                "followUpId": "550e8400-e29b-41d4-a716-446655440001",
                "to": ["jane@acme.com"],
                "subject": "Great meeting today — next steps",
                "htmlBody": "<p>Hi Jane, thanks for your time today...</p>",
                "textBody": "Hi Jane, thanks for your time today...",
                "templateType": "followUp"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Email sent successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "deliveryId": { "type": "string", "format": "uuid" },
                    "messageId": { "type": "string" },
                    "status": { "type": "string", "enum": ["sent"] }
                  }
                },
                "example": {
                  "success": true,
                  "deliveryId": "550e8400-e29b-41d4-a716-446655440002",
                  "messageId": "re_abc123",
                  "status": "sent"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "422": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": {
                  "error": "Validation failed",
                  "details": { "to": ["At least one recipient is required"] }
                }
              }
            }
          },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/proposals": {
      "get": {
        "tags": ["Proposals"],
        "operationId": "listProposals",
        "summary": "List proposals",
        "description": "Returns a paginated list of proposals for the authenticated tenant. Supports filtering by status, client name, and date range, plus full-text search across title and client name.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "default": 1 }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": ["draft", "review", "approved", "sent", "accepted", "rejected"]
            }
          },
          {
            "name": "clientName",
            "in": "query",
            "schema": { "type": "string", "maxLength": 255 },
            "description": "Partial client name match (case-insensitive)."
          },
          {
            "name": "search",
            "in": "query",
            "schema": { "type": "string", "maxLength": 255 },
            "description": "Full-text search across title and client name."
          },
          {
            "name": "dateFrom",
            "in": "query",
            "schema": { "type": "string", "format": "date-time" }
          },
          {
            "name": "dateTo",
            "in": "query",
            "schema": { "type": "string", "format": "date-time" }
          },
          {
            "name": "sortBy",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": ["createdAt", "updatedAt", "title", "clientName", "status"],
              "default": "createdAt"
            }
          },
          {
            "name": "sortOrder",
            "in": "query",
            "schema": { "type": "string", "enum": ["asc", "desc"], "default": "desc" }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of proposals.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/Proposal" } },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "page": { "type": "integer" },
                        "limit": { "type": "integer" },
                        "total": { "type": "integer" },
                        "totalPages": { "type": "integer" }
                      }
                    },
                    "message": { "type": "string" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      },
      "post": {
        "tags": ["Proposals"],
        "operationId": "createProposal",
        "summary": "Create proposal",
        "description": "Creates a new proposal. If a `crmDealId` is provided and a CRM connection is active, FastFollow will attempt to enrich the client name and metadata from the CRM deal automatically.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["title", "clientName"],
                "properties": {
                  "title": { "type": "string", "minLength": 1, "maxLength": 500 },
                  "clientName": { "type": "string", "minLength": 1, "maxLength": 255 },
                  "status": {
                    "type": "string",
                    "enum": ["draft", "review", "approved", "sent", "accepted", "rejected"],
                    "default": "draft"
                  },
                  "templateType": { "type": "string", "maxLength": 100, "default": "custom" },
                  "sections": {
                    "type": "array",
                    "items": { "$ref": "#/components/schemas/ProposalSection" },
                    "default": []
                  },
                  "pricing": { "$ref": "#/components/schemas/Pricing" },
                  "validUntil": { "type": "string", "format": "date-time" },
                  "crmDealId": { "type": "string", "maxLength": 255 },
                  "metadata": { "type": "object", "additionalProperties": true, "default": {} }
                }
              },
              "example": {
                "title": "Enterprise Proposal — Acme Corp Q2",
                "clientName": "Acme Corp",
                "status": "draft",
                "templateType": "enterprise",
                "sections": [{ "title": "Executive Summary", "content": "This proposal outlines...", "order": 0 }],
                "pricing": {
                  "items": [{ "name": "Enterprise License", "quantity": 10, "unitPrice": 299, "total": 2990 }],
                  "subtotal": 2990,
                  "tax": 239.2,
                  "total": 3229.2,
                  "currency": "USD"
                },
                "validUntil": "2026-06-30T23:59:59.000Z"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Proposal created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "data": { "$ref": "#/components/schemas/Proposal" },
                    "message": { "type": "string" }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/proposals/{id}": {
      "get": {
        "tags": ["Proposals"],
        "operationId": "getProposal",
        "summary": "Get proposal",
        "description": "Retrieves a single proposal by its UUID. The proposal must belong to the authenticated tenant.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "200": {
            "description": "Proposal retrieved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "data": { "$ref": "#/components/schemas/Proposal" },
                    "message": { "type": "string" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      },
      "put": {
        "tags": ["Proposals"],
        "operationId": "updateProposal",
        "summary": "Update proposal",
        "description": "Performs a partial update on a proposal. Only include the fields you want to change. All fields are optional.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": { "type": "string", "minLength": 1, "maxLength": 500 },
                  "clientName": { "type": "string", "minLength": 1, "maxLength": 255 },
                  "status": {
                    "type": "string",
                    "enum": ["draft", "review", "approved", "sent", "accepted", "rejected"]
                  },
                  "templateType": { "type": "string", "maxLength": 100 },
                  "sections": {
                    "type": "array",
                    "items": { "$ref": "#/components/schemas/ProposalSection" }
                  },
                  "pricing": { "$ref": "#/components/schemas/Pricing" },
                  "validUntil": { "type": "string", "format": "date-time", "nullable": true },
                  "crmDealId": { "type": "string", "maxLength": 255, "nullable": true },
                  "metadata": { "type": "object", "additionalProperties": true }
                },
                "additionalProperties": false
              },
              "example": { "status": "sent" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Proposal updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "data": { "$ref": "#/components/schemas/Proposal" },
                    "message": { "type": "string" }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      },
      "delete": {
        "tags": ["Proposals"],
        "operationId": "deleteProposal",
        "summary": "Delete proposal",
        "description": "Soft-deletes a proposal by setting its `deletedAt` timestamp. The proposal is excluded from all future list queries but can be recovered by support if needed.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "200": {
            "description": "Proposal deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": { "type": "string" },
                        "deletedAt": { "type": "string", "format": "date-time" }
                      }
                    },
                    "message": { "type": "string" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/proposals/{id}/export": {
      "post": {
        "tags": ["Proposals"],
        "operationId": "exportProposal",
        "summary": "Export proposal",
        "description": "Generates an exportable version of a proposal in either JSON or HTML format. The HTML export is a self-contained document suitable for print or PDF conversion.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "format": {
                    "type": "string",
                    "enum": ["json", "html"],
                    "default": "html",
                    "description": "`json` returns structured data; `html` returns a self-contained HTML string."
                  },
                  "branding": {
                    "type": "object",
                    "properties": {
                      "companyName": { "type": "string", "maxLength": 200 },
                      "primaryColor": {
                        "type": "string",
                        "pattern": "^#[0-9a-fA-F]{6}$",
                        "default": "#3b82f6"
                      },
                      "secondaryColor": {
                        "type": "string",
                        "pattern": "^#[0-9a-fA-F]{6}$",
                        "default": "#64748b"
                      }
                    }
                  }
                }
              },
              "example": {
                "format": "html",
                "branding": {
                  "companyName": "FastFollow",
                  "primaryColor": "#16a34a",
                  "secondaryColor": "#475569"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Export generated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "data": {
                      "type": "object",
                      "properties": {
                        "proposalId": { "type": "string" },
                        "format": { "type": "string", "enum": ["html", "json"] },
                        "content": {
                          "type": "string",
                          "description": "HTML string (when format=html) or full JSON object (when format=json)."
                        },
                        "filename": { "type": "string" },
                        "generatedAt": { "type": "string", "format": "date-time" }
                      }
                    },
                    "message": { "type": "string" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/reports/analytics": {
      "get": {
        "tags": ["Reports"],
        "operationId": "getAnalytics",
        "summary": "Get analytics data",
        "description": "Returns aggregated email, meeting, and workflow analytics for the authenticated tenant. Results are cached for 5 minutes on the CDN. Use `timeRange=custom` with `startDate`/`endDate` for arbitrary date ranges.",
        "parameters": [
          {
            "name": "timeRange",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": ["7d", "30d", "90d", "custom"],
              "default": "30d"
            }
          },
          {
            "name": "startDate",
            "in": "query",
            "description": "Required when `timeRange=custom`. ISO date string.",
            "schema": { "type": "string", "format": "date-time" }
          },
          {
            "name": "endDate",
            "in": "query",
            "description": "Required when `timeRange=custom`. ISO date string.",
            "schema": { "type": "string", "format": "date-time" }
          }
        ],
        "responses": {
          "200": {
            "description": "Analytics data.",
            "headers": {
              "Cache-Control": {
                "schema": { "type": "string" },
                "description": "CDN cache directive: s-maxage=300, stale-while-revalidate=60"
              }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AnalyticsResponse" },
                "example": {
                  "meta": {
                    "tenantId": "tenant_01",
                    "timeRange": "30d",
                    "startDate": "2026-02-17T00:00:00.000Z",
                    "endDate": "2026-03-19T00:00:00.000Z",
                    "generatedAt": "2026-03-19T10:00:00.000Z"
                  },
                  "summary": {
                    "totalFollowUpsSent": 142,
                    "responseRate": 34.5,
                    "meetingsProcessed": 38,
                    "avgTimeToSendMinutes": 4,
                    "workflowCompletionRate": 97.2
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/demo/setup": {
      "post": {
        "tags": ["Demo"],
        "operationId": "createDemoEnvironment",
        "summary": "Create demo environment",
        "description": "Public endpoint — no authentication required. Creates a sandboxed demo tenant pre-populated with sample data and returns short-lived credentials (24-hour expiry). Rate-limited to 5 requests per IP per hour.",
        "security": [],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "prospectEmail": { "type": "string", "format": "email" },
                  "prospectName": { "type": "string", "maxLength": 255 },
                  "companyName": { "type": "string", "maxLength": 255 },
                  "source": { "type": "string", "maxLength": 100, "description": "UTM or referral source." }
                }
              },
              "example": {
                "prospectEmail": "you@company.com",
                "prospectName": "Alex Johnson",
                "companyName": "Contoso Ltd",
                "source": "website"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Demo environment created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "message": { "type": "string" },
                    "demo": {
                      "type": "object",
                      "properties": {
                        "loginUrl": { "type": "string", "format": "uri" },
                        "email": { "type": "string", "format": "email" },
                        "password": { "type": "string" },
                        "expiresAt": { "type": "string", "format": "date-time" },
                        "expiresInHours": { "type": "integer", "example": 24 }
                      }
                    },
                    "stats": { "type": "object", "additionalProperties": true },
                    "notice": { "type": "string" }
                  }
                },
                "example": {
                  "success": true,
                  "message": "Demo environment ready",
                  "demo": {
                    "loginUrl": "https://app.fastfollow.ai/auth/signin?demo=true",
                    "email": "demo-user@fastfollow.demo",
                    "password": "Demo!2026",
                    "expiresAt": "2026-03-20T10:00:00.000Z",
                    "expiresInHours": 24
                  },
                  "notice": "This is a sandboxed demo environment with pre-populated sample data."
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": {
            "description": "Rate limit exceeded — 5 demo environments per IP per hour.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Too many demo requests from this IP. Please try again in one hour."
                }
              }
            }
          },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/calendar/upcoming": {
      "get": {
        "tags": ["Calendar"],
        "operationId": "getUpcomingEvents",
        "summary": "Get upcoming calendar events",
        "description": "Returns the authenticated user's upcoming Google Calendar events for the next 1–30 days (default 7), enriched with Google Meet links and participant details. Requires an active Google OAuth connection.",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "description": "Number of days ahead to fetch (1–30).",
            "schema": { "type": "integer", "minimum": 1, "maximum": 30, "default": 7 }
          }
        ],
        "responses": {
          "200": {
            "description": "Upcoming events retrieved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/UpcomingMeeting" }
                    },
                    "total": { "type": "integer" },
                    "connected": { "type": "boolean" }
                  }
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "eventId": "evt_abc123",
                      "title": "Discovery call — Beta Inc",
                      "startTime": "2026-03-20T15:00:00.000Z",
                      "endTime": "2026-03-20T15:30:00.000Z",
                      "participantCount": 3,
                      "isGoogleMeet": true,
                      "meetLink": "https://meet.google.com/abc-def-ghi"
                    }
                  ],
                  "total": 1,
                  "connected": true
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": {
            "description": "No active Google Calendar connection.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": {
                  "error": "No Google connection",
                  "message": "Connect Google Calendar to see upcoming meetings",
                  "connected": false
                }
              }
            }
          },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/objection/sessions": {
      "get": {
        "tags": ["Objection Coaching"],
        "operationId": "listObjectionSessions",
        "summary": "List objection coaching sessions",
        "description": "Returns a paginated list of AI-generated objection coaching sessions for the authenticated user, ordered by most recent first.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": { "type": "integer", "minimum": 0, "default": 0 }
          }
        ],
        "responses": {
          "200": {
            "description": "Coaching sessions retrieved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/CoachingSession" }
                    },
                    "total": { "type": "integer" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      },
      "post": {
        "tags": ["Objection Coaching"],
        "operationId": "createObjectionSession",
        "summary": "Create objection coaching session",
        "description": "Analyzes the transcript of an ingested meeting and extracts objections by type (price, timing, competition, authority, feature). Returns structured coaching data with suggested handling frameworks and responses for each objection.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["meetingId"],
                "properties": {
                  "meetingId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The UUID of an ingested meeting whose transcript will be analyzed."
                  }
                }
              },
              "example": { "meetingId": "550e8400-e29b-41d4-a716-446655440000" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Coaching session created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "data": { "$ref": "#/components/schemas/CoachingSession" }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/user/onboarding": {
      "get": {
        "tags": ["Onboarding"],
        "operationId": "getOnboardingState",
        "summary": "Get onboarding state",
        "description": "Returns the current onboarding progress for the authenticated user, including step completion status and overall progress percentage.",
        "responses": {
          "200": {
            "description": "Onboarding state retrieved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "data": { "$ref": "#/components/schemas/OnboardingState" },
                    "message": { "type": "string" }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "status": "in_progress",
                    "progress": 60,
                    "completedAt": null,
                    "completedSteps": [1, 2, 3],
                    "steps": [
                      {
                        "step": 1,
                        "title": "Connect your CRM",
                        "description": "Link HubSpot or Salesforce",
                        "completed": true
                      },
                      {
                        "step": 2,
                        "title": "Connect Google",
                        "description": "Authorize calendar and Meet",
                        "completed": true
                      },
                      {
                        "step": 3,
                        "title": "Send your first follow-up",
                        "description": "Approve an AI-drafted email",
                        "completed": true
                      },
                      {
                        "step": 4,
                        "title": "Invite your team",
                        "description": "Add colleagues to your workspace",
                        "completed": false
                      },
                      {
                        "step": 5,
                        "title": "Set up workflows",
                        "description": "Configure automation rules",
                        "completed": false
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      },
      "put": {
        "tags": ["Onboarding"],
        "operationId": "updateOnboardingState",
        "summary": "Update onboarding state",
        "description": "Marks a specific onboarding step as completed and optionally stores step-specific data. Automatically advances the overall progress and marks onboarding complete when all 5 steps are finished.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["completedStep"],
                "properties": {
                  "completedStep": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Step number to mark as completed (1–5)."
                  },
                  "stepData": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Optional key-value data to store with the completed step."
                  }
                }
              },
              "example": { "completedStep": 3, "stepData": { "followUpId": "fu_abc123" } }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Onboarding state updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "data": {
                      "type": "object",
                      "properties": {
                        "success": { "type": "boolean" },
                        "isComplete": { "type": "boolean" },
                        "nextStep": { "type": "integer", "nullable": true },
                        "message": { "type": "string" }
                      }
                    },
                    "message": { "type": "string" }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "success": true,
                    "isComplete": false,
                    "nextStep": 4,
                    "message": "Step 3 completed. Continue to step 4."
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/followups/{id}/approve": {
      "post": {
        "tags": ["Follow-ups"],
        "operationId": "approveFollowUp",
        "summary": "Approve a follow-up",
        "description": "Approves an AI-generated follow-up item and immediately sends the follow-up email via Resend. The email send is fire-and-forget — the approval response is returned immediately without waiting for delivery confirmation.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The follow-up item ID.",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Follow-up approved and email dispatched.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "message": { "type": "string" },
                    "item": {
                      "type": "object",
                      "description": "The updated follow-up item with status set to `completed`.",
                      "additionalProperties": true
                    }
                  }
                },
                "example": {
                  "success": true,
                  "message": "Follow-up approved and marked as sent",
                  "item": {
                    "id": "fu_abc123",
                    "status": "completed",
                    "email_sent_at": "2026-03-19T10:00:00.000Z"
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    }
  }
}
