{
  "openapi": "3.0.3",
  "info": {
    "title": "Convo API",
    "version": "1.0.0",
    "description": "The Convo API gives your code the sales calls Convo records: transcripts, key points, coaching feedback, follow-up emails, contact briefings and webhooks. Build integrations with Zapier, Make or your own applications.\n\n## Authentication\nSend `Authorization: Bearer convo_...` with a key created in the Convo desktop app under **Settings → Convo API**. Keys work on Starter, Professional and Enterprise plans.\n\n## Plans\n| | Starter | Professional | Enterprise |\n|---|---|---|---|\n| API keys | 1 | 3 | 10 |\n| Requests per hour | 100 | 500 | 2,000 |\n| AI credits per month | 1,500 | 15,000 | 30,000 |\n| Webhooks | No | 3 | 999 |\n| Contacts (Convo Memory) | No | With Memory | With Memory |\n\nThe desktop app's AI features use the same credits. See https://docs.itsconvo.com/tiers.\n\n## Rate limits\nSuccessful responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`. Over the hourly limit you get `429 rate_limited` with `Retry-After`.",
    "contact": {
      "email": "ivan@itsconvo.com"
    },
    "termsOfService": "https://www.itsconvo.com/terms/api"
  },
  "servers": [
    {
      "url": "https://www.itsconvo.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Conversations",
      "description": "List, read, rename, share and delete your recorded calls"
    },
    {
      "name": "AI Generation",
      "description": "Key points, coaching feedback, answers and follow-up emails. Feedback, query and email use AI credits from your monthly budget. Key points are free."
    },
    {
      "name": "Calendar",
      "description": "Upcoming events from your connected Google or Outlook calendar"
    },
    {
      "name": "User",
      "description": "Your profile, plan and AI usage"
    },
    {
      "name": "Webhooks",
      "description": "Signed POST requests to your URL when something happens in Convo (Professional and Enterprise)"
    },
    {
      "name": "Contacts",
      "description": "Convo Memory: the people you meet, their briefings and open action items (Professional and Enterprise, with Memory enabled)"
    }
  ],
  "paths": {
    "/api/v1/conversations": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "List conversations",
        "description": "Returns your recorded calls, newest first. `search` matches the title or any words in the transcript, case-insensitive. `since` keeps only transcript from that time on, so for a call that started earlier, `startTime`, `duration` and `segmentCount` cover only the later part. `pagination.total` counts every matching call.",
        "operationId": "listConversations",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100,
              "minimum": 1
            },
            "description": "Calls per page, 1 to 100. Larger values are lowered to 100."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "description": "Number of calls to skip"
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Matches the title or transcript text, case-insensitive"
          },
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only transcript from this time on (ISO 8601)"
          }
        ],
        "responses": {
          "200": {
            "description": "List of conversations",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Conversation"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "session_1773842400000",
                      "title": "Q1 Planning Review",
                      "startTime": "2026-03-18T14:00:00Z",
                      "endTime": "2026-03-18T14:45:00Z",
                      "duration": 45,
                      "segmentCount": 128
                    }
                  ],
                  "pagination": {
                    "total": 42,
                    "limit": 20,
                    "offset": 0
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/conversations/{id}/transcript": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "Get transcript",
        "description": "Returns the transcript recorded during the call, oldest segment first. Returns 404 for a call that doesn't exist or was deleted.",
        "operationId": "getTranscript",
        "parameters": [
          {
            "$ref": "#/components/parameters/conversationId"
          }
        ],
        "responses": {
          "200": {
            "description": "Conversation transcript",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "segments": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Segment"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "session_1773842400000",
                    "segments": [
                      {
                        "text": "Let's review the Q1 targets.",
                        "speaker": "local",
                        "timestamp": "2026-03-18T14:01:12.000Z"
                      },
                      {
                        "text": "Revenue is up 15% from last quarter.",
                        "speaker": "remote",
                        "timestamp": "2026-03-18T14:01:18.000Z"
                      },
                      {
                        "text": "That's great progress. What about retention?",
                        "speaker": "local",
                        "timestamp": "2026-03-18T14:01:25.000Z"
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/conversations/{id}/keypoints": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "Get cached keypoints",
        "description": "Returns the saved key points. The desktop app generates them after each call. Returns 404 when the call has none yet.",
        "operationId": "getKeypoints",
        "parameters": [
          {
            "$ref": "#/components/parameters/conversationId"
          }
        ],
        "responses": {
          "200": {
            "description": "Cached keypoints",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/KeypointsResponse"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "session_1773842400000",
                    "title": "Q1 Planning Review",
                    "keypoints": {
                      "title": "Q1 Planning Review",
                      "sections": [
                        {
                          "heading": "Revenue Performance",
                          "points": [
                            "Revenue increased 15% quarter-over-quarter",
                            "Enterprise segment grew fastest at 22%"
                          ]
                        },
                        {
                          "heading": "Customer Retention",
                          "points": [
                            "Retention rate held steady at 94%",
                            "Churn concentrated in SMB segment"
                          ]
                        }
                      ],
                      "decisions": [
                        "Increase enterprise sales team by 2 headcount",
                        "Launch SMB retention campaign by end of April"
                      ],
                      "actionItems": [
                        {
                          "item": "Draft enterprise hiring plan",
                          "owner": "Alice",
                          "confidence": 0.95,
                          "priority": "high"
                        },
                        {
                          "item": "Prepare SMB retention proposal",
                          "owner": "Bob",
                          "confidence": 0.87,
                          "priority": "medium"
                        }
                      ],
                      "participants": [
                        "Alice",
                        "Bob"
                      ],
                      "importantDates": [
                        {
                          "date": "2026-04-30",
                          "context": "Deadline for SMB retention campaign launch"
                        }
                      ]
                    },
                    "createdAt": "2026-03-18T15:00:00.000Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "AI Generation"
        ],
        "summary": "Generate keypoints",
        "description": "Returns the saved key points, or generates them when there are none. `forceRefresh: true` generates them again. Key points don't use AI credits. Generating fires `conversation.keypoints.ready`, plus `conversation.summary.ready` when there are sections and `conversation.action_items.ready` when there are action items.",
        "operationId": "generateKeypoints",
        "parameters": [
          {
            "$ref": "#/components/parameters/conversationId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "forceRefresh": {
                    "type": "boolean",
                    "default": false,
                    "description": "Force regeneration even if cached result exists"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Generated keypoints",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/KeypointsResponse"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "cached": {
                              "type": "boolean"
                            }
                          }
                        }
                      ]
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "session_1773842400000",
                    "title": "Q1 Planning Review",
                    "keypoints": {
                      "title": "Q1 Planning Review",
                      "sections": [
                        {
                          "heading": "Revenue Performance",
                          "points": [
                            "Revenue increased 15% quarter-over-quarter",
                            "Enterprise segment grew fastest at 22%"
                          ]
                        },
                        {
                          "heading": "Customer Retention",
                          "points": [
                            "Retention rate held steady at 94%",
                            "Churn concentrated in SMB segment"
                          ]
                        }
                      ],
                      "decisions": [
                        "Increase enterprise sales team by 2 headcount",
                        "Launch SMB retention campaign by end of April"
                      ],
                      "actionItems": [
                        {
                          "item": "Draft enterprise hiring plan",
                          "owner": "Alice",
                          "confidence": 0.95,
                          "priority": "high"
                        },
                        {
                          "item": "Prepare SMB retention proposal",
                          "owner": "Bob",
                          "confidence": 0.87,
                          "priority": "medium"
                        }
                      ],
                      "participants": [
                        "Alice",
                        "Bob"
                      ],
                      "importantDates": [
                        {
                          "date": "2026-04-30",
                          "context": "Deadline for SMB retention campaign launch"
                        }
                      ]
                    },
                    "createdAt": "2026-03-18T15:00:00.000Z",
                    "cached": false
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/conversations/{id}/feedback": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "Get cached feedback",
        "description": "Returns the saved coaching feedback for the call. Returns 404 when none has been generated.",
        "operationId": "getFeedback",
        "parameters": [
          {
            "$ref": "#/components/parameters/conversationId"
          }
        ],
        "responses": {
          "200": {
            "description": "Cached feedback",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/FeedbackResponse"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "session_1773842400000",
                    "feedback": {
                      "overallScore": 8.5,
                      "topPriority": {
                        "focus": "Pause before answering Bob's pushback",
                        "quote": "Yeah no, we already considered that.",
                        "reason": "The two interruptions both came when Bob raised retention risk, and both shut the topic down before he finished the thought. Holding the pause is the single change that unblocks the retention conversation next call.",
                        "alternative": "That's a fair concern, Bob. Walk me through what you're seeing."
                      },
                      "strengths": [
                        {
                          "category": "clarity",
                          "description": "Clear and structured presentation of quarterly results",
                          "timestamp": "14:01",
                          "quote": "Let's review the Q1 targets one by one."
                        }
                      ],
                      "growthAreas": [
                        {
                          "category": "listening",
                          "description": "Interrupted Bob twice during the retention discussion",
                          "suggestion": "Pause for 2-3 seconds after others finish speaking before responding.",
                          "timestamp": "14:12"
                        }
                      ],
                      "categoryScores": {
                        "clarity": 9,
                        "listening": 7.5,
                        "timeManagement": 8.5,
                        "collaboration": 8,
                        "decisionMaking": 9
                      },
                      "talkListenRatio": 45,
                      "actionPlan": [
                        "Practice active listening by pausing before responding",
                        "Summarize others' points before adding your own"
                      ]
                    },
                    "overallScore": 8.5,
                    "userSpeakerName": "local",
                    "createdAt": "2026-03-18T15:00:00.000Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "AI Generation"
        ],
        "summary": "Generate feedback",
        "description": "Returns the saved feedback, or generates it when there is none. `forceRefresh: true` generates it again. Generating uses one AI credit. It is counted before the transcript is checked, so a 404, or a 400 for a call with fewer than 10 transcript segments, also uses one. Fires `conversation.feedback.ready`.",
        "operationId": "generateFeedback",
        "parameters": [
          {
            "$ref": "#/components/parameters/conversationId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "forceRefresh": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Generated feedback",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/FeedbackResponse"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "cached": {
                              "type": "boolean"
                            }
                          }
                        }
                      ]
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "session_1773842400000",
                    "feedback": {
                      "overallScore": 8.5,
                      "topPriority": {
                        "focus": "Pause before answering Bob's pushback",
                        "quote": "Yeah no, we already considered that.",
                        "reason": "The two interruptions both came when Bob raised retention risk, and both shut the topic down before he finished the thought. Holding the pause is the single change that unblocks the retention conversation next call.",
                        "alternative": "That's a fair concern, Bob. Walk me through what you're seeing."
                      },
                      "strengths": [
                        {
                          "category": "clarity",
                          "description": "Clear and structured presentation of quarterly results",
                          "timestamp": "14:01",
                          "quote": "Let's review the Q1 targets one by one."
                        }
                      ],
                      "growthAreas": [
                        {
                          "category": "listening",
                          "description": "Interrupted Bob twice during the retention discussion",
                          "suggestion": "Pause for 2-3 seconds after others finish speaking before responding.",
                          "timestamp": "14:12"
                        }
                      ],
                      "categoryScores": {
                        "clarity": 9,
                        "listening": 7.5,
                        "timeManagement": 8.5,
                        "collaboration": 8,
                        "decisionMaking": 9
                      },
                      "talkListenRatio": 45,
                      "actionPlan": [
                        "Practice active listening by pausing before responding",
                        "Summarize others' points before adding your own"
                      ],
                      "stats": {
                        "talkListenRatio": 45,
                        "totalMinutes": 45
                      }
                    },
                    "overallScore": 8.5,
                    "userSpeakerName": "local",
                    "createdAt": "2026-03-18T15:00:00.000Z",
                    "cached": false
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/AILimitExceeded"
          }
        }
      }
    },
    "/api/v1/conversations/{id}/query": {
      "post": {
        "tags": [
          "AI Generation"
        ],
        "summary": "Query conversation",
        "description": "Answers a question about one call from its transcript. Each request uses one AI credit. A call that doesn't exist returns 404 without using one. `suggestedQueries` are fixed follow-up questions picked from words in your question.",
        "operationId": "queryConversation",
        "parameters": [
          {
            "$ref": "#/components/parameters/conversationId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "query"
                ],
                "properties": {
                  "query": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "The question to ask",
                    "example": "What were the main action items?"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "AI-generated answer",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/QueryResponse"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "session_1773842400000",
                    "query": "What were the main action items?",
                    "response": "Two action items were identified: (1) Alice will draft an enterprise hiring plan by next week, and (2) Bob will prepare an SMB retention proposal by end of April.",
                    "metadata": {
                      "segmentsAnalyzed": 187,
                      "conversationDuration": 45
                    },
                    "suggestedQueries": [
                      "Who is responsible for each task?",
                      "What are the deadlines?",
                      "Draft an email summarizing this meeting"
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/AILimitExceeded"
          }
        }
      }
    },
    "/api/v1/conversations/{id}/email": {
      "post": {
        "tags": [
          "AI Generation"
        ],
        "summary": "Generate follow-up email",
        "description": "Drafts a follow-up email from the call. Each request uses one AI credit. A call that doesn't exist returns 404 without using one. The body is optional.",
        "operationId": "generateEmail",
        "parameters": [
          {
            "$ref": "#/components/parameters/conversationId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "recipientName": {
                    "type": "string",
                    "description": "Name of the email recipient",
                    "example": "Sarah"
                  },
                  "tone": {
                    "type": "string",
                    "enum": [
                      "professional",
                      "casual",
                      "concise"
                    ],
                    "default": "professional",
                    "description": "`professional`, `casual` or `concise`"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Generated email",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/EmailResponse"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "session_1773842400000",
                    "subject": "Follow-up: Q1 Planning Review",
                    "body": "Hi Sarah,\n\nThanks for joining the Q1 Planning Review today. Here are the key takeaways:\n\n- Revenue increased 15% quarter-over-quarter, with enterprise leading at 22% growth\n- Customer retention held at 94%, though SMB churn needs attention\n\nAction items:\n- Alice: Draft enterprise hiring plan by next week\n- Bob: Prepare SMB retention proposal by end of April\n\nLet me know if I missed anything.\n\nBest,\nAlice",
                    "keyPoints": [
                      "Revenue increased 15% quarter-over-quarter",
                      "Enterprise segment grew fastest at 22%",
                      "Retention rate held steady at 94%"
                    ],
                    "actionItems": [
                      "Draft enterprise hiring plan (Alice, next week)",
                      "Prepare SMB retention proposal (Bob, end of April)"
                    ],
                    "generatedAt": "2026-03-18T15:05:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/AILimitExceeded"
          }
        }
      }
    },
    "/api/v1/conversations/{id}/share": {
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Share conversation",
        "description": "Makes the call readable by anyone with the link and returns the link. Fires `conversation.shared`.",
        "operationId": "shareConversation",
        "parameters": [
          {
            "$ref": "#/components/parameters/conversationId"
          }
        ],
        "responses": {
          "200": {
            "description": "Share URL generated",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "session_1773842400000",
                    "shareUrl": "https://www.itsconvo.com/shared/conversation/session_1773842400000"
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "shareUrl": {
                          "type": "string",
                          "format": "uri"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/conversations/{id}": {
      "patch": {
        "tags": [
          "Conversations"
        ],
        "summary": "Update conversation",
        "description": "Renames the call. Spaces around the title are removed. The title is stored with the key points, so a call without key points returns 404. Fires `conversation.updated`.",
        "operationId": "updateConversation",
        "parameters": [
          {
            "$ref": "#/components/parameters/conversationId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "title"
                ],
                "properties": {
                  "title": {
                    "type": "string",
                    "description": "New conversation title",
                    "example": "Acme pilot pricing call"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Conversation updated",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "session_1773842400000",
                    "title": "Acme pilot pricing call"
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "title": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Conversations"
        ],
        "summary": "Delete conversation",
        "description": "Deletes the call. It no longer appears in the API or the app, and it is unlinked from your Convo Memory contacts. The API can't restore it. Fires `conversation.deleted`.",
        "operationId": "deleteConversation",
        "parameters": [
          {
            "$ref": "#/components/parameters/conversationId"
          }
        ],
        "responses": {
          "200": {
            "description": "Conversation deleted",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "session_1773842400000",
                    "deleted": true
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "deleted": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/calendar/events": {
      "get": {
        "tags": [
          "Calendar"
        ],
        "summary": "List calendar events",
        "description": "Returns upcoming events from the calendar connected in the Convo desktop app, Google or Outlook, by start time. Events come in the provider's own format. Declined and cancelled events are left out. Without `timeMax`, Outlook looks 7 days ahead and Google has no end.",
        "operationId": "listCalendarEvents",
        "parameters": [
          {
            "name": "maxResults",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 10,
              "minimum": 1,
              "maximum": 50
            },
            "description": "Events to return, 1 to 50"
          },
          {
            "name": "timeMin",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Start of the window (ISO 8601). Defaults to now."
          },
          {
            "name": "timeMax",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "End of the window (ISO 8601)"
          }
        ],
        "responses": {
          "200": {
            "description": "Calendar events",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CalendarEvent"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "evt_abc123",
                      "summary": "Weekly Standup",
                      "start": {
                        "dateTime": "2026-03-19T09:00:00-04:00",
                        "timeZone": "America/New_York"
                      },
                      "end": {
                        "dateTime": "2026-03-19T09:30:00-04:00",
                        "timeZone": "America/New_York"
                      },
                      "attendees": [
                        {
                          "email": "alice@example.com",
                          "displayName": "Alice Chen",
                          "responseStatus": "accepted"
                        },
                        {
                          "email": "bob@example.com",
                          "displayName": "Bob Smith",
                          "responseStatus": "accepted"
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Calendar access was revoked. Reconnect the calendar in the desktop app.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "forbidden",
                    "message": "Calendar permissions revoked. Please reconnect your calendar."
                  }
                }
              }
            }
          },
          "404": {
            "description": "No calendar is connected",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "Calendar not connected. Connect your calendar in the Convo dashboard."
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/user/profile": {
      "get": {
        "tags": [
          "User"
        ],
        "summary": "Get profile and usage",
        "description": "Returns your profile, your plan and how many AI credits you have used this month.",
        "operationId": "getUserProfile",
        "responses": {
          "200": {
            "description": "User profile and usage stats",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/UserProfile"
                    }
                  }
                },
                "example": {
                  "data": {
                    "profile": {
                      "name": "Alice Chen",
                      "email": "alice@example.com",
                      "timezone": "America/New_York",
                      "language": "en",
                      "profilePictureUrl": null,
                      "createdAt": "2025-09-15T10:00:00.000Z"
                    },
                    "subscription": {
                      "tier": "professional",
                      "status": "active",
                      "currentPeriodEnd": "2026-04-15T00:00:00.000Z"
                    },
                    "apiUsage": {
                      "aiGenerations": {
                        "used": 1240,
                        "limit": 15000,
                        "remaining": 13760,
                        "resetsAt": "2026-04-01T00:00:00.000Z"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhooks",
        "description": "Lists your active webhooks, newest first. Secrets are not returned. Professional and Enterprise.",
        "operationId": "listWebhooks",
        "responses": {
          "200": {
            "description": "Active webhooks",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Webhook"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "8d3f6a52-1c4e-4b7a-9e2d-5f0b8c1a7e34",
                      "url": "https://hooks.example.com/convo",
                      "events": [
                        "conversation.keypoints.ready",
                        "transcript.ready"
                      ],
                      "created_at": "2026-03-10T12:00:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Webhooks need a Professional or Enterprise plan, or the webhook limit is reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "forbidden",
                    "message": "Webhooks require a Professional plan or above."
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Register webhook",
        "description": "Registers a URL for the events you choose. The URL must use HTTPS and can't point to localhost or a private network. Professional plans can have 3 webhooks and Enterprise 999. The `secret` appears only in this response.",
        "operationId": "createWebhook",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook created",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookCreated"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "wh_def456",
                    "url": "https://hooks.example.com/convo",
                    "events": [
                      "conversation.keypoints.ready"
                    ],
                    "secret": "whsec_a1b2c3d4e5f6...",
                    "createdAt": "2026-03-18T16:00:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Webhooks need a Professional or Enterprise plan, or the webhook limit is reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "forbidden",
                    "message": "Webhooks require a Professional plan or above."
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete webhook",
        "description": "Deletes a webhook. Send its `id` in the JSON body. Works on any paid plan, so you can remove webhooks after a downgrade.",
        "operationId": "deleteWebhook",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Webhook ID to delete"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook deleted",
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "8d3f6a52-1c4e-4b7a-9e2d-5f0b8c1a7e34",
                    "deleted": true
                  }
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/webhooks/test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Test a webhook",
        "description": "Sends a signed `webhook.test` event to one of your webhooks and returns your server's status code. `delivered` is true whenever your server answered, whatever the status, so check `statusCode`. If your server doesn't answer within 10 seconds, the response is 200 with `delivered: false`.",
        "operationId": "testWebhook",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "webhookId"
                ],
                "properties": {
                  "webhookId": {
                    "type": "string",
                    "description": "ID of the webhook to test",
                    "example": "8d3f6a52-1c4e-4b7a-9e2d-5f0b8c1a7e34"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Test result",
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "webhookId": "8d3f6a52-1c4e-4b7a-9e2d-5f0b8c1a7e34",
                    "delivered": true,
                    "statusCode": 200
                  }
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Webhooks need a Professional or Enterprise plan, or the webhook limit is reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "forbidden",
                    "message": "Webhooks require a Professional plan or above."
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/contacts": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "List contacts",
        "description": "The people Convo Memory has met in your calendar meetings, most meetings first, then most recently seen. Requires a Professional or Enterprise plan with Convo Memory enabled. Without Memory, the list is empty and `reason` is `no-brain`.",
        "operationId": "listContacts",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 200
            },
            "description": "Max results to return"
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            },
            "description": "Number of results to skip"
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Matches email or display name, case-insensitive"
          }
        ],
        "responses": {
          "200": {
            "description": "List of contacts",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Contact"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    },
                    "reason": {
                      "type": "string",
                      "enum": [
                        "no-brain"
                      ],
                      "description": "Present when Convo Memory is not enabled on the account"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "contact_3f2b8c1e-7d4a-4c1b-9a6e-2b5d8f0c4e71",
                      "email": "sarah@northwind.com",
                      "displayName": "Sarah Lee",
                      "companyDomain": "northwind.com",
                      "meetingCount": 4,
                      "firstSeenAt": "2026-08-12T15:00:00Z",
                      "lastSeenAt": "2026-09-29T14:00:00Z"
                    }
                  ],
                  "pagination": {
                    "total": 37,
                    "limit": 50,
                    "offset": 0
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/contacts/unresolved-threads": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "List unresolved threads",
        "description": "Open action items from meetings with your contacts, oldest meeting first, so the ones waiting longest come first. Items you marked resolved or dismissed are left out, and so are meetings not matched to a contact. Requires a Professional or Enterprise plan with Convo Memory enabled. Without Memory, the list is empty and `reason` is `no-brain`.",
        "operationId": "listUnresolvedThreads",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 25,
              "minimum": 1,
              "maximum": 100
            },
            "description": "Max results to return"
          }
        ],
        "responses": {
          "200": {
            "description": "Open action items",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/UnresolvedThread"
                      }
                    },
                    "total": {
                      "type": "integer",
                      "description": "The number of items returned"
                    },
                    "reason": {
                      "type": "string",
                      "enum": [
                        "no-brain"
                      ],
                      "description": "Present when Convo Memory is not enabled on the account"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "sessionId": "session_1773842400000",
                      "actionItem": "Send the pilot pricing to Sarah",
                      "hash": "9b2f6c0d4e8a1f3b5c7d9e0a2b4c6d8e",
                      "meetingTitle": "Northwind pilot review",
                      "createdAt": "2026-09-26T14:00:00Z",
                      "stalenessDays": 9,
                      "contactId": "contact_3f2b8c1e-7d4a-4c1b-9a6e-2b5d8f0c4e71",
                      "contactName": "Sarah Lee",
                      "contactCompany": "northwind.com"
                    }
                  ],
                  "total": 1
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/contacts/{id}/briefing": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "Get a contact briefing",
        "description": "Everything Convo Memory has on one contact before your next call: the relationship summary, recent meetings, open action items and recent decisions. Requires a Professional or Enterprise plan with Convo Memory enabled. Without Memory, it returns 403. A contact from another account returns 404.",
        "operationId": "getContactBriefing",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Contact ID from List contacts",
            "example": "contact_3f2b8c1e-7d4a-4c1b-9a6e-2b5d8f0c4e71"
          },
          {
            "name": "historyLimit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 25,
              "minimum": 1,
              "maximum": 50
            },
            "description": "How many recent meetings to read for the briefing"
          }
        ],
        "responses": {
          "200": {
            "description": "The briefing",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ContactBriefing"
                    }
                  }
                },
                "example": {
                  "data": {
                    "contact": {
                      "id": "contact_3f2b8c1e-7d4a-4c1b-9a6e-2b5d8f0c4e71",
                      "email": "sarah@northwind.com",
                      "displayName": "Sarah Lee",
                      "companyDomain": "northwind.com",
                      "meetingCount": 4,
                      "firstSeenAt": "2026-08-12T15:00:00Z",
                      "lastSeenAt": "2026-09-29T14:00:00Z"
                    },
                    "summary": {
                      "prose": "Sarah leads operations at Northwind and is evaluating a three-month pilot.",
                      "topics": [],
                      "open_questions": [
                        "Who signs off on the pilot budget?"
                      ]
                    },
                    "summaryGeneratedAt": "2026-09-29T15:10:00Z",
                    "recentMeetings": [
                      {
                        "sessionId": "session_1773842400000",
                        "title": "Northwind pilot review",
                        "date": "2026-09-26T14:00:00Z"
                      }
                    ],
                    "openActionItems": [
                      {
                        "item": "Send the pilot pricing to Sarah",
                        "sessionId": "session_1773842400000",
                        "hash": "9b2f6c0d4e8a1f3b5c7d9e0a2b4c6d8e",
                        "owner": "Alex Kim",
                        "priority": "high",
                        "meetingDate": "2026-09-26T14:00:00Z",
                        "meetingTitle": "Northwind pilot review"
                      }
                    ],
                    "recentDecisions": [
                      "Start the pilot with the support team"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API key",
        "description": "A Convo API key: `convo_` followed by 32 hex characters. Create it in the Convo desktop app under Settings → Convo API."
      }
    },
    "parameters": {
      "conversationId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Conversation ID from List conversations",
        "example": "session_1773842400000"
      }
    },
    "headers": {
      "X-RateLimit-Limit": {
        "schema": {
          "type": "integer"
        },
        "description": "Requests allowed per hour on your plan"
      },
      "X-RateLimit-Remaining": {
        "schema": {
          "type": "integer"
        },
        "description": "Requests left this hour"
      },
      "X-RateLimit-Reset": {
        "schema": {
          "type": "integer"
        },
        "description": "Unix time in seconds of the next full hour (UTC), when the count resets"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "unauthorized",
                  "forbidden",
                  "not_found",
                  "rate_limited",
                  "ai_limit_exceeded",
                  "validation_error",
                  "internal_error"
                ]
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer",
            "example": 42
          },
          "limit": {
            "type": "integer",
            "example": 20
          },
          "offset": {
            "type": "integer",
            "example": 0
          }
        }
      },
      "Conversation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "session_1773842400000",
            "description": "The call's ID"
          },
          "title": {
            "type": "string",
            "example": "Q1 Planning Review",
            "nullable": true,
            "description": "Null when the call has no key points yet"
          },
          "startTime": {
            "type": "string",
            "format": "date-time"
          },
          "endTime": {
            "type": "string",
            "format": "date-time"
          },
          "duration": {
            "type": "number",
            "description": "Minutes between the first and last segment"
          },
          "segmentCount": {
            "type": "integer"
          }
        }
      },
      "Segment": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "example": "Let's review the Q1 targets."
          },
          "speaker": {
            "type": "string",
            "example": "local",
            "nullable": true,
            "description": "`local` is you, from your microphone. Other people are usually `remote`."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "KeypointsResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string",
            "example": "Q1 Planning Review"
          },
          "keypoints": {
            "type": "object",
            "properties": {
              "title": {
                "type": "string"
              },
              "sections": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "heading": {
                      "type": "string"
                    },
                    "points": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "decisions": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "actionItems": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "item": {
                      "type": "string"
                    },
                    "owner": {
                      "type": "string"
                    },
                    "confidence": {
                      "type": "number"
                    },
                    "priority": {
                      "type": "string",
                      "enum": [
                        "high",
                        "medium",
                        "low"
                      ]
                    }
                  }
                }
              },
              "participants": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "importantDates": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "date": {
                      "type": "string"
                    },
                    "context": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "FeedbackResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "feedback": {
            "type": "object",
            "properties": {
              "overallScore": {
                "type": "number",
                "example": 8.5,
                "description": "0 to 10"
              },
              "topPriority": {
                "type": "object",
                "description": "Optional. The single highest-leverage focus area to act on before the next call — synthesized from across the growth areas, not necessarily a copy of growthAreas[0]. Absent when the call was too brief/routine for a clear priority, or in feedback generated before this field existed.",
                "properties": {
                  "focus": {
                    "type": "string",
                    "description": "Short imperative theme, 4-8 words ('Slow down before committing to scope')."
                  },
                  "quote": {
                    "type": "string",
                    "description": "Verbatim anchoring moment from the user's transcript."
                  },
                  "reason": {
                    "type": "string",
                    "description": "Why this is the #1 priority for next call (1-2 sentences)."
                  },
                  "alternative": {
                    "type": "string",
                    "description": "Verbatim line the user could have said instead. Rendered as a 'Try saying' callout."
                  }
                }
              },
              "strengths": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "category": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "timestamp": {
                      "type": "string"
                    },
                    "quote": {
                      "type": "string",
                      "description": "Verbatim line the user said (primary content for chat-bubble rendering)."
                    },
                    "reason": {
                      "type": "string",
                      "description": "Why this specific moment landed well (1-2 sentences)."
                    }
                  }
                }
              },
              "growthAreas": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "category": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "suggestion": {
                      "type": "string"
                    },
                    "timestamp": {
                      "type": "string"
                    },
                    "quote": {
                      "type": "string",
                      "description": "Verbatim line the user said that could be improved."
                    },
                    "reason": {
                      "type": "string",
                      "description": "Why this moment fell short (1-2 sentences)."
                    },
                    "alternative": {
                      "type": "string",
                      "description": "Verbatim better phrasing the user could have used instead. Rendered as a 'Try saying' callout."
                    }
                  }
                }
              },
              "categoryScores": {
                "type": "object",
                "properties": {
                  "clarity": {
                    "type": "number"
                  },
                  "listening": {
                    "type": "number"
                  },
                  "timeManagement": {
                    "type": "number"
                  },
                  "collaboration": {
                    "type": "number"
                  },
                  "decisionMaking": {
                    "type": "number"
                  }
                }
              },
              "talkListenRatio": {
                "type": "number",
                "description": "Talk-to-listen ratio as a percentage (0 to 100), estimated by the model",
                "example": 45
              },
              "actionPlan": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "stats": {
                "type": "object",
                "description": "Added when the feedback is generated through the API",
                "properties": {
                  "talkListenRatio": {
                    "type": "number",
                    "description": "Same as `talkListenRatio`, 50 when the model gave none"
                  },
                  "totalMinutes": {
                    "type": "integer",
                    "description": "Minutes between the first and last segment"
                  }
                }
              }
            }
          },
          "overallScore": {
            "type": "number"
          },
          "userSpeakerName": {
            "type": "string",
            "description": "The speaker Convo scored as you: `local` (your microphone), a speaker whose name matches yours, or `Unknown`"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "QueryResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "query": {
            "type": "string"
          },
          "response": {
            "type": "string"
          },
          "metadata": {
            "type": "object",
            "properties": {
              "segmentsAnalyzed": {
                "type": "integer"
              },
              "conversationDuration": {
                "type": "number",
                "description": "Duration in minutes"
              }
            }
          },
          "suggestedQueries": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "EmailResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "subject": {
            "type": "string",
            "example": "Follow-up: Q1 Planning Review"
          },
          "body": {
            "type": "string"
          },
          "keyPoints": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "actionItems": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "generatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CalendarEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "summary": {
            "type": "string",
            "example": "Weekly Standup"
          },
          "start": {
            "type": "object",
            "properties": {
              "dateTime": {
                "type": "string",
                "format": "date-time"
              },
              "timeZone": {
                "type": "string"
              }
            }
          },
          "end": {
            "type": "object",
            "properties": {
              "dateTime": {
                "type": "string",
                "format": "date-time"
              },
              "timeZone": {
                "type": "string"
              }
            }
          },
          "attendees": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "email": {
                  "type": "string"
                },
                "displayName": {
                  "type": "string"
                },
                "responseStatus": {
                  "type": "string"
                }
              }
            }
          }
        },
        "description": "An event in the calendar provider's own format, passed through unchanged. Google Calendar events look like this. Outlook events are Microsoft Graph events, with `subject`, `start`, `end` and `attendees[].emailAddress` instead.",
        "additionalProperties": true
      },
      "UserProfile": {
        "type": "object",
        "properties": {
          "profile": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "email": {
                "type": "string"
              },
              "timezone": {
                "type": "string"
              },
              "language": {
                "type": "string"
              },
              "profilePictureUrl": {
                "type": "string",
                "nullable": true
              },
              "createdAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "subscription": {
            "type": "object",
            "properties": {
              "tier": {
                "type": "string",
                "enum": [
                  "starter",
                  "professional",
                  "enterprise"
                ]
              },
              "status": {
                "type": "string",
                "nullable": true
              },
              "currentPeriodEnd": {
                "type": "string",
                "nullable": true,
                "format": "date-time"
              }
            }
          },
          "apiUsage": {
            "type": "object",
            "properties": {
              "aiGenerations": {
                "type": "object",
                "properties": {
                  "used": {
                    "type": "integer",
                    "description": "AI credits used this month (UTC), including by the desktop app"
                  },
                  "limit": {
                    "type": "integer",
                    "description": "AI credits per month on your plan"
                  },
                  "remaining": {
                    "type": "integer",
                    "description": "`limit` minus `used`. A daily cap of a third of `limit`, or the monthly spending cap, can stop generation earlier."
                  },
                  "resetsAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "The first day of next month, 00:00 UTC"
                  }
                }
              }
            }
          }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "8d3f6a52-1c4e-4b7a-9e2d-5f0b8c1a7e34",
            "description": "Webhook ID"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "example": "https://hooks.example.com/convo"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEvent"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookCreated": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEvent"
            }
          },
          "secret": {
            "type": "string",
            "description": "Signing secret: `whsec_` followed by 48 hex characters. Shown only in this response.",
            "example": "whsec_3c9f0e1a7b2d4c6e8f0a1b3c5d7e9f1a2b4c6d8e0f1a3b5c"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookCreateRequest": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "An HTTPS URL. Localhost and private network addresses are refused.",
            "example": "https://hooks.example.com/convo"
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/WebhookEvent"
            }
          }
        }
      },
      "WebhookEvent": {
        "type": "string",
        "enum": [
          "conversation.created",
          "conversation.updated",
          "conversation.keypoints.ready",
          "conversation.summary.ready",
          "conversation.action_items.ready",
          "conversation.feedback.ready",
          "conversation.shared",
          "conversation.deleted",
          "transcript.ready",
          "transcript.failed",
          "webhook.test"
        ]
      },
      "Contact": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "contact_3f2b8c1e-7d4a-4c1b-9a6e-2b5d8f0c4e71"
          },
          "email": {
            "type": "string",
            "example": "sarah@northwind.com"
          },
          "displayName": {
            "type": "string",
            "nullable": true,
            "example": "Sarah Lee"
          },
          "companyDomain": {
            "type": "string",
            "nullable": true,
            "description": "The email domain; null for personal email providers",
            "example": "northwind.com"
          },
          "meetingCount": {
            "type": "integer",
            "example": 4
          },
          "firstSeenAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lastSeenAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "UnresolvedThread": {
        "type": "object",
        "properties": {
          "sessionId": {
            "type": "string",
            "description": "The conversation the action item came from",
            "example": "session_1773842400000"
          },
          "actionItem": {
            "type": "string",
            "example": "Send the pilot pricing to Sarah"
          },
          "hash": {
            "type": "string",
            "description": "Identifies the action item within its conversation"
          },
          "meetingTitle": {
            "type": "string",
            "nullable": true,
            "example": "Northwind pilot review"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the meeting started"
          },
          "stalenessDays": {
            "type": "integer",
            "description": "Days since the meeting",
            "example": 9
          },
          "contactId": {
            "type": "string",
            "example": "contact_3f2b8c1e-7d4a-4c1b-9a6e-2b5d8f0c4e71"
          },
          "contactName": {
            "type": "string",
            "nullable": true,
            "example": "Sarah Lee"
          },
          "contactCompany": {
            "type": "string",
            "nullable": true,
            "description": "The contact's company domain",
            "example": "northwind.com"
          }
        }
      },
      "BriefingActionItem": {
        "type": "object",
        "properties": {
          "item": {
            "type": "string",
            "example": "Send the pilot pricing to Sarah"
          },
          "sessionId": {
            "type": "string",
            "example": "session_1773842400000"
          },
          "hash": {
            "type": "string",
            "description": "Identifies the action item within its conversation"
          },
          "owner": {
            "type": "string",
            "nullable": true
          },
          "priority": {
            "type": "string",
            "nullable": true,
            "enum": [
              "high",
              "medium",
              "low"
            ]
          },
          "meetingDate": {
            "type": "string",
            "format": "date-time"
          },
          "meetingTitle": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "ContactBriefing": {
        "type": "object",
        "properties": {
          "contact": {
            "$ref": "#/components/schemas/Contact"
          },
          "summary": {
            "type": "object",
            "nullable": true,
            "description": "What Convo Memory knows about this relationship, or null before a summary exists",
            "properties": {
              "prose": {
                "type": "string"
              },
              "topics": {
                "type": "array",
                "items": {}
              },
              "open_questions": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "summaryGeneratedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "recentMeetings": {
            "type": "array",
            "description": "The most recent meetings with this contact, newest first",
            "items": {
              "type": "object",
              "properties": {
                "sessionId": {
                  "type": "string"
                },
                "title": {
                  "type": "string",
                  "nullable": true
                },
                "date": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "openActionItems": {
            "type": "array",
            "description": "Up to 10 open action items from those meetings, newest meeting first",
            "items": {
              "$ref": "#/components/schemas/BriefingActionItem"
            }
          },
          "recentDecisions": {
            "type": "array",
            "description": "Up to 10 decisions from those meetings, without repeats",
            "items": {
              "type": "string"
            }
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "The Authorization header is missing, or the key is wrong or revoked",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "unauthorized",
                "message": "Missing or invalid Authorization header. Use: Bearer <api_key>"
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The plan doesn't allow this request, or the key only works with the MCP server",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "forbidden",
                "message": "API access requires a Starter plan or above. Upgrade at https://www.itsconvo.com/pricing"
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "not_found",
                "message": "Conversation not found"
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Over your hourly request limit",
        "headers": {
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          },
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds until the next full hour, when the count resets"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "rate_limited",
                "message": "Rate limit exceeded (100 requests per hour for starter plan)"
              }
            }
          }
        }
      },
      "AILimitExceeded": {
        "description": "Your monthly, daily or spending limit for AI credits is reached. The model provider being busy returns `429 rate_limited` instead, without headers.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "ai_limit_exceeded",
                "message": "Monthly AI generation limit reached. Upgrade your plan for more."
              }
            }
          }
        }
      },
      "ValidationError": {
        "description": "A field or parameter is missing or wrong. The message names it.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "validation_error",
                "message": "title is required and must be a non-empty string"
              }
            }
          }
        }
      }
    }
  }
}
