{
	"openapi": "3.1.0",
	"info": {
		"title": "SpeakNotes API",
		"version": "1.1.0",
		"summary": "Transcription, summarization, and note management for applications and AI agents.",
		"description": "The SpeakNotes API turns audio, video, and YouTube links into transcripts and structured summaries, and reads and writes the note library behind them.\n\n## Authentication\nSend an API key as a bearer token, or an OAuth 2.1 access token. Any signed-in account can create a key at https://speaknotes.io/settings/api-keys, and it works immediately.\n\n## Sandbox\nKeys that begin `sn_test_` run every route against fixed output: no model calls, no credits, and writes land in a separate sandbox library. The response shape is identical to live.\n\n## Credits\nOne credit is one started minute of media. Free accounts get 60 credits a month and Pro accounts get 2,000; beyond that credits are prepaid. Reads are free. Every operation documents its cost in `x-speaknotes-credits`, and every charging response returns `creditsCharged` plus an `X-Credits-Remaining` header.\n\n## Rate limits\n120 reads, 30 writes, and 10 processing requests per minute, counted per credential. Responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset`; a 429 carries `Retry-After`.\n\n## Errors\nEvery failure returns the same JSON envelope with a stable `code`, a human `message`, and a `doc_url`. The codes are listed at https://speaknotes.io/developers/errors.",
		"termsOfService": "https://speaknotes.io/terms",
		"license": {
			"name": "SpeakNotes API terms",
			"url": "https://speaknotes.io/terms"
		},
		"contact": {
			"name": "SpeakNotes support",
			"url": "https://speaknotes.io/contact",
			"email": "hello@speaknotes.io"
		}
	},
	"externalDocs": {
		"description": "SpeakNotes developer platform",
		"url": "https://speaknotes.io/developers"
	},
	"servers": [
		{
			"url": "https://api.speaknotes.io",
			"description": "Production"
		}
	],
	"tags": [
		{
			"name": "Discovery",
			"description": "Describe the API without a credential."
		},
		{
			"name": "Notes",
			"description": "Read and write notes, transcripts, and summaries."
		},
		{
			"name": "Folders",
			"description": "Organize notes into folders."
		},
		{
			"name": "Processing",
			"description": "Operations that run a model and spend credits."
		},
		{
			"name": "Usage",
			"description": "Credit balance and spending history."
		}
	],
	"security": [
		{
			"BearerAuth": []
		}
	],
	"paths": {
		"/v1": {
			"get": {
				"tags": [
					"Discovery"
				],
				"operationId": "getApiIndex",
				"summary": "Describe the API",
				"description": "Returns the resources, scopes, rate limits, credit costs, and authentication options. This is the only operation that needs no credential, so an agent can orient itself before it has a key.",
				"security": [],
				"responses": {
					"200": {
						"description": "The API description",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiIndex"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					}
				}
			}
		},
		"/v1/health": {
			"get": {
				"tags": [
					"Discovery"
				],
				"operationId": "getHealth",
				"summary": "Check the API is up",
				"description": "A liveness check that needs no credential and never fails on account state.",
				"security": [],
				"responses": {
					"200": {
						"description": "The API is serving requests",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Health"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					}
				}
			}
		},
		"/v1/notes": {
			"get": {
				"tags": [
					"Notes"
				],
				"operationId": "listNotes",
				"summary": "List notes",
				"description": "Returns notes newest first. Filter by folder, status, type, or last update. Reads are free: no credits are charged.",
				"x-speaknotes-credits": 0,
				"parameters": [
					{
						"in": "query",
						"name": "updatedAfter",
						"description": "Only return notes updated after this ISO 8601 timestamp.",
						"schema": {
							"type": "string",
							"format": "date-time"
						}
					},
					{
						"in": "query",
						"name": "folderId",
						"description": "Only return notes filed under this folder.",
						"schema": {
							"type": "string"
						}
					},
					{
						"in": "query",
						"name": "status",
						"description": "Filter by processing status.",
						"schema": {
							"type": "string",
							"enum": [
								"Done",
								"Summarizing",
								"Error"
							]
						}
					},
					{
						"in": "query",
						"name": "type",
						"description": "Filter by note type, such as audio, video, youtube, pdf, or import.",
						"schema": {
							"type": "string"
						}
					},
					{
						"in": "query",
						"name": "limit",
						"description": "How many notes to return.",
						"schema": {
							"type": "integer",
							"default": 50,
							"minimum": 1,
							"maximum": 100
						}
					},
					{
						"in": "query",
						"name": "offset",
						"description": "How many notes to skip.",
						"schema": {
							"type": "integer",
							"default": 0,
							"minimum": 0
						}
					}
				],
				"responses": {
					"200": {
						"description": "A page of notes",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/NoteList"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"notes:read"
						]
					}
				]
			},
			"post": {
				"tags": [
					"Notes"
				],
				"operationId": "createNote",
				"summary": "Create a note from text",
				"description": "Saves text or markdown into the library as a finished note. Use this to import content you already have. Free: no model runs, so no credits are charged.",
				"x-speaknotes-credits": 0,
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/CreateNoteRequest"
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "The created note",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Note"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"notes:write"
						]
					}
				]
			}
		},
		"/v1/notes/{id}": {
			"parameters": [
				{
					"in": "path",
					"name": "id",
					"required": true,
					"description": "The note id.",
					"schema": {
						"type": "string"
					}
				}
			],
			"get": {
				"tags": [
					"Notes"
				],
				"operationId": "getNote",
				"summary": "Get a note",
				"description": "Returns one note in full, including its transcript, summary, and structured content.",
				"x-speaknotes-credits": 0,
				"responses": {
					"200": {
						"description": "The note",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Note"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"404": {
						"$ref": "#/components/responses/NotFound"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"notes:read"
						]
					}
				]
			},
			"patch": {
				"tags": [
					"Notes"
				],
				"operationId": "updateNote",
				"summary": "Update a note",
				"description": "Changes the title, summary, folder, or pinned state. Send only the fields you want changed; a null folderId moves the note to the top level.",
				"x-speaknotes-credits": 0,
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/UpdateNoteRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "The updated note",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Note"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"404": {
						"$ref": "#/components/responses/NotFound"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"notes:write"
						]
					}
				]
			},
			"delete": {
				"tags": [
					"Notes"
				],
				"operationId": "deleteNote",
				"summary": "Delete a note",
				"description": "Permanently deletes the note and any media stored with it. This cannot be undone.",
				"x-speaknotes-credits": 0,
				"responses": {
					"204": {
						"description": "The note was deleted"
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"404": {
						"$ref": "#/components/responses/NotFound"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"notes:write"
						]
					}
				]
			}
		},
		"/v1/notes/{id}/status": {
			"parameters": [
				{
					"in": "path",
					"name": "id",
					"required": true,
					"description": "The note id.",
					"schema": {
						"type": "string"
					}
				}
			],
			"get": {
				"tags": [
					"Notes"
				],
				"operationId": "getNoteStatus",
				"summary": "Get processing status",
				"description": "Poll this after starting an upload. isComplete turns true when the transcript and summary are ready; hasError turns true when processing failed, and errorCode says why.",
				"x-speaknotes-credits": 0,
				"responses": {
					"200": {
						"description": "The processing status",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/NoteStatus"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"404": {
						"$ref": "#/components/responses/NotFound"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"notes:read"
						]
					}
				]
			}
		},
		"/v1/notes/{id}/transcript": {
			"parameters": [
				{
					"in": "path",
					"name": "id",
					"required": true,
					"description": "The note id.",
					"schema": {
						"type": "string"
					}
				}
			],
			"get": {
				"tags": [
					"Notes"
				],
				"operationId": "getNoteTranscript",
				"summary": "Get a transcript",
				"description": "Returns the transcript on its own, without the summary. Answers 409 when the note is still processing.",
				"x-speaknotes-credits": 0,
				"responses": {
					"200": {
						"description": "The transcript",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Transcript"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"404": {
						"$ref": "#/components/responses/NotFound"
					},
					"409": {
						"$ref": "#/components/responses/Conflict"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"notes:read"
						]
					}
				]
			}
		},
		"/v1/notes/{id}/questions": {
			"parameters": [
				{
					"in": "path",
					"name": "id",
					"required": true,
					"description": "The note id.",
					"schema": {
						"type": "string"
					}
				}
			],
			"post": {
				"tags": [
					"Notes"
				],
				"operationId": "askNoteQuestion",
				"summary": "Ask a question about a note",
				"description": "Answers a question from the note's transcript. Costs 1 credit.",
				"x-speaknotes-credits": 1,
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/QuestionRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "The answer",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Answer"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"402": {
						"$ref": "#/components/responses/InsufficientCredits"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"404": {
						"$ref": "#/components/responses/NotFound"
					},
					"409": {
						"$ref": "#/components/responses/Conflict"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					},
					"502": {
						"$ref": "#/components/responses/UpstreamFailure"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"notes:read",
							"summaries:write"
						]
					}
				]
			}
		},
		"/v1/folders": {
			"get": {
				"tags": [
					"Folders"
				],
				"operationId": "listFolders",
				"summary": "List folders",
				"description": "Returns every folder in the account, ordered by name.",
				"x-speaknotes-credits": 0,
				"responses": {
					"200": {
						"description": "The folders",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/FolderList"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"folders:read"
						]
					}
				]
			},
			"post": {
				"tags": [
					"Folders"
				],
				"operationId": "createFolder",
				"summary": "Create a folder",
				"description": "Creates a folder that notes can be filed under.",
				"x-speaknotes-credits": 0,
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/CreateFolderRequest"
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "The created folder",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Folder"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"folders:write"
						]
					}
				]
			}
		},
		"/v1/folders/{id}": {
			"parameters": [
				{
					"in": "path",
					"name": "id",
					"required": true,
					"description": "The folder id.",
					"schema": {
						"type": "string"
					}
				}
			],
			"get": {
				"tags": [
					"Folders"
				],
				"operationId": "getFolder",
				"summary": "Get a folder",
				"description": "Returns one folder.",
				"x-speaknotes-credits": 0,
				"responses": {
					"200": {
						"description": "The folder",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Folder"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"404": {
						"$ref": "#/components/responses/NotFound"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"folders:read"
						]
					}
				]
			},
			"patch": {
				"tags": [
					"Folders"
				],
				"operationId": "updateFolder",
				"summary": "Rename a folder",
				"description": "Changes the folder's name or color.",
				"x-speaknotes-credits": 0,
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/UpdateFolderRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "The updated folder",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Folder"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"404": {
						"$ref": "#/components/responses/NotFound"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"folders:write"
						]
					}
				]
			},
			"delete": {
				"tags": [
					"Folders"
				],
				"operationId": "deleteFolder",
				"summary": "Delete a folder",
				"description": "Deletes the folder. Notes inside it are kept and moved back to the top level, never deleted.",
				"x-speaknotes-credits": 0,
				"responses": {
					"204": {
						"description": "The folder was deleted"
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"404": {
						"$ref": "#/components/responses/NotFound"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"folders:write"
						]
					}
				]
			}
		},
		"/v1/uploads": {
			"post": {
				"tags": [
					"Processing"
				],
				"operationId": "createUpload",
				"summary": "Start a media upload",
				"description": "Creates a note and returns a signed URL to PUT the file to. Nothing is charged here: credits are taken when you complete the upload and the real duration is known.",
				"x-speaknotes-credits": 0,
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/CreateUploadRequest"
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "The upload target",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Upload"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"413": {
						"$ref": "#/components/responses/PayloadTooLarge"
					},
					"415": {
						"$ref": "#/components/responses/UnsupportedMediaType"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"transcriptions:write",
							"notes:write"
						]
					}
				]
			}
		},
		"/v1/uploads/{id}/complete": {
			"parameters": [
				{
					"in": "path",
					"name": "id",
					"required": true,
					"description": "The note id returned by createUpload.",
					"schema": {
						"type": "string"
					}
				}
			],
			"post": {
				"tags": [
					"Processing"
				],
				"operationId": "completeUpload",
				"summary": "Finish an upload and start processing",
				"description": "Confirms the file arrived, charges 1 credit per started minute of media, and starts transcription and summarization in the background. Poll getNoteStatus until it reports complete. Calling this twice is safe: the second call reports the upload as already completed and charges nothing.",
				"x-speaknotes-credits": "1 per started minute",
				"responses": {
					"200": {
						"description": "The upload was already completed",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/UploadAccepted"
								}
							}
						}
					},
					"202": {
						"description": "Processing started",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/UploadAccepted"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"402": {
						"$ref": "#/components/responses/InsufficientCredits"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"404": {
						"$ref": "#/components/responses/NotFound"
					},
					"409": {
						"$ref": "#/components/responses/Conflict"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					},
					"502": {
						"$ref": "#/components/responses/UpstreamFailure"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"transcriptions:write",
							"notes:write"
						]
					}
				]
			}
		},
		"/v1/transcriptions": {
			"post": {
				"tags": [
					"Processing"
				],
				"operationId": "createTranscription",
				"summary": "Transcribe audio from a URL",
				"description": "Downloads a publicly reachable https URL and returns the transcript inline. Costs 1 credit per started minute. Private, loopback, and link-local addresses are refused.",
				"x-speaknotes-credits": "1 per started minute",
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/TranscriptionRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "The transcript",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TranscriptionResult"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"402": {
						"$ref": "#/components/responses/InsufficientCredits"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					},
					"502": {
						"$ref": "#/components/responses/UpstreamFailure"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"transcriptions:write"
						]
					}
				]
			}
		},
		"/v1/summaries": {
			"post": {
				"tags": [
					"Processing"
				],
				"operationId": "createSummary",
				"summary": "Summarize text",
				"description": "Turns a transcript or any long text into a structured summary in the chosen style. Costs 2 credits.",
				"x-speaknotes-credits": 2,
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/SummaryRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "The summary",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/SummaryResult"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"402": {
						"$ref": "#/components/responses/InsufficientCredits"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"422": {
						"$ref": "#/components/responses/ContentPolicy"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					},
					"502": {
						"$ref": "#/components/responses/UpstreamFailure"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"summaries:write"
						]
					}
				]
			}
		},
		"/v1/youtube-summaries": {
			"post": {
				"tags": [
					"Processing"
				],
				"operationId": "createYoutubeSummary",
				"summary": "Summarize a YouTube video",
				"description": "Summarizes a YouTube URL directly, without downloading it yourself. Costs 1 credit per started minute of video.",
				"x-speaknotes-credits": "1 per started minute",
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/YoutubeSummaryRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "The summary",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/YoutubeSummaryResult"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"402": {
						"$ref": "#/components/responses/InsufficientCredits"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					},
					"502": {
						"$ref": "#/components/responses/UpstreamFailure"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"summaries:write"
						]
					}
				]
			}
		},
		"/v1/usage": {
			"get": {
				"tags": [
					"Usage"
				],
				"operationId": "getUsage",
				"summary": "Get credit balance and usage",
				"description": "Returns the current balance, what has been spent this period, recent charges, and what each operation costs. Check this before starting a long job.",
				"x-speaknotes-credits": 0,
				"parameters": [
					{
						"in": "query",
						"name": "limit",
						"description": "How many recent charges to return.",
						"schema": {
							"type": "integer",
							"default": 50,
							"minimum": 1,
							"maximum": 100
						}
					}
				],
				"responses": {
					"200": {
						"description": "The usage summary",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Usage"
								}
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/InvalidRequest"
					},
					"401": {
						"$ref": "#/components/responses/InvalidToken"
					},
					"403": {
						"$ref": "#/components/responses/InsufficientScope"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"500": {
						"$ref": "#/components/responses/InternalError"
					}
				},
				"security": [
					{
						"BearerAuth": []
					},
					{
						"OAuth2": [
							"usage:read"
						]
					}
				]
			}
		}
	},
	"components": {
		"securitySchemes": {
			"BearerAuth": {
				"type": "http",
				"scheme": "bearer",
				"bearerFormat": "APIKey",
				"description": "A SpeakNotes API key (sn_live_ or sn_test_) sent as a bearer token. Create one at https://speaknotes.io/settings/api-keys. Firebase ID tokens from a signed-in session are also accepted."
			},
			"OAuth2": {
				"type": "oauth2",
				"description": "OAuth 2.1 with PKCE, for apps acting on behalf of other SpeakNotes users. Clients may register themselves at https://api.speaknotes.io/oauth/register.",
				"flows": {
					"authorizationCode": {
						"authorizationUrl": "https://speaknotes.io/oauth/authorize",
						"tokenUrl": "https://api.speaknotes.io/oauth/token",
						"refreshUrl": "https://api.speaknotes.io/oauth/token",
						"scopes": {
							"notes:read": "Read notes, transcripts, and summaries.",
							"notes:write": "Create, update, and delete notes.",
							"folders:read": "Read folders.",
							"folders:write": "Create, update, and delete folders.",
							"transcriptions:write": "Transcribe audio and video.",
							"summaries:write": "Generate summaries from audio, text, and YouTube URLs.",
							"usage:read": "Read credit balance and usage history."
						}
					}
				}
			}
		},
		"schemas": {
			"Error": {
				"type": "object",
				"required": [
					"error"
				],
				"properties": {
					"error": {
						"type": "object",
						"required": [
							"type",
							"code",
							"message",
							"doc_url"
						],
						"properties": {
							"type": {
								"type": "string",
								"description": "The class of failure, mirroring the HTTP status family."
							},
							"code": {
								"type": "string",
								"enum": [
									"invalid_request",
									"invalid_token",
									"insufficient_scope",
									"not_found",
									"method_not_allowed",
									"conflict",
									"payload_too_large",
									"unsupported_media_type",
									"insufficient_credits",
									"rate_limited",
									"upstream_failure",
									"content_policy",
									"internal_error"
								],
								"description": "Stable identifier for the failure. Safe to branch on."
							},
							"message": {
								"type": "string",
								"description": "Human-readable explanation. May change."
							},
							"doc_url": {
								"type": "string",
								"format": "uri",
								"description": "Where this code is documented."
							},
							"details": {
								"type": "array",
								"description": "Present when specific fields are at fault.",
								"items": {
									"type": "object",
									"properties": {
										"field": {
											"type": "string"
										},
										"issue": {
											"type": "string"
										}
									}
								}
							},
							"retry_after_seconds": {
								"type": "integer",
								"description": "How long to wait before retrying, when retrying can help."
							},
							"request_id": {
								"type": "string",
								"description": "Quote this to support."
							}
						}
					}
				}
			},
			"ApiIndex": {
				"type": "object",
				"properties": {
					"name": {
						"type": "string"
					},
					"version": {
						"type": "string"
					},
					"documentation": {
						"type": "string",
						"format": "uri"
					},
					"openapi": {
						"type": "string",
						"format": "uri"
					},
					"mcp": {
						"type": "string",
						"format": "uri"
					},
					"authentication": {
						"type": "object",
						"additionalProperties": true
					},
					"sandbox": {
						"type": "object",
						"additionalProperties": true
					},
					"credits": {
						"type": "object",
						"additionalProperties": true
					},
					"rateLimits": {
						"type": "object",
						"additionalProperties": true
					},
					"scopes": {
						"type": "array",
						"items": {
							"type": "object",
							"properties": {
								"scope": {
									"type": "string"
								},
								"description": {
									"type": "string"
								}
							}
						}
					},
					"resources": {
						"type": "object",
						"additionalProperties": {
							"type": "string"
						}
					}
				}
			},
			"Health": {
				"type": "object",
				"properties": {
					"status": {
						"type": "string",
						"enum": [
							"ok"
						]
					},
					"version": {
						"type": "string"
					}
				}
			},
			"Note": {
				"type": "object",
				"required": [
					"id"
				],
				"properties": {
					"id": {
						"type": "string"
					},
					"title": {
						"type": "string"
					},
					"summary": {
						"type": "string",
						"description": "The generated summary, in the note's style."
					},
					"originalTranscription": {
						"type": "string",
						"description": "The full transcript."
					},
					"structuredContent": {
						"type": "object",
						"additionalProperties": true,
						"description": "Machine-readable form of the summary, keyed by format."
					},
					"status": {
						"type": "string",
						"enum": [
							"Done",
							"Summarizing",
							"Error"
						]
					},
					"processingStage": {
						"type": [
							"string",
							"null"
						]
					},
					"type": {
						"type": "string"
					},
					"styleId": {
						"type": "string"
					},
					"language": {
						"type": [
							"string",
							"null"
						]
					},
					"folderId": {
						"type": [
							"string",
							"null"
						]
					},
					"isPinned": {
						"type": "boolean"
					},
					"source": {
						"type": "string"
					},
					"dateCreated": {
						"type": "string",
						"format": "date-time"
					},
					"updatedAt": {
						"type": "string",
						"format": "date-time"
					}
				}
			},
			"NoteList": {
				"type": "object",
				"required": [
					"data",
					"pagination"
				],
				"properties": {
					"data": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/Note"
						}
					},
					"pagination": {
						"type": "object",
						"properties": {
							"limit": {
								"type": "integer"
							},
							"offset": {
								"type": "integer"
							},
							"hasMore": {
								"type": "boolean"
							}
						}
					}
				}
			},
			"CreateNoteRequest": {
				"type": "object",
				"required": [
					"title",
					"content"
				],
				"properties": {
					"title": {
						"type": "string",
						"maxLength": 500
					},
					"content": {
						"type": "string",
						"description": "Text or markdown body."
					},
					"folderId": {
						"type": [
							"string",
							"null"
						]
					},
					"source": {
						"type": "string",
						"description": "Where the content came from, for your own records."
					},
					"format": {
						"type": "string",
						"description": "Structured content format label."
					}
				}
			},
			"UpdateNoteRequest": {
				"type": "object",
				"minProperties": 1,
				"properties": {
					"title": {
						"type": "string",
						"maxLength": 500
					},
					"summary": {
						"type": "string"
					},
					"folderId": {
						"type": [
							"string",
							"null"
						]
					},
					"isPinned": {
						"type": "boolean"
					}
				}
			},
			"NoteStatus": {
				"type": "object",
				"properties": {
					"status": {
						"type": "string",
						"enum": [
							"Done",
							"Summarizing",
							"Error",
							"Unknown"
						]
					},
					"processingStage": {
						"type": [
							"string",
							"null"
						]
					},
					"isComplete": {
						"type": "boolean"
					},
					"hasError": {
						"type": "boolean"
					},
					"errorMessage": {
						"type": [
							"string",
							"null"
						]
					},
					"errorCode": {
						"type": [
							"string",
							"null"
						]
					}
				}
			},
			"Transcript": {
				"type": "object",
				"properties": {
					"noteId": {
						"type": "string"
					},
					"transcript": {
						"type": "string"
					},
					"language": {
						"type": [
							"string",
							"null"
						]
					}
				}
			},
			"QuestionRequest": {
				"type": "object",
				"required": [
					"question"
				],
				"properties": {
					"question": {
						"type": "string",
						"maxLength": 2000
					}
				}
			},
			"Answer": {
				"type": "object",
				"properties": {
					"noteId": {
						"type": "string"
					},
					"question": {
						"type": "string"
					},
					"answer": {
						"type": "string"
					},
					"creditsCharged": {
						"type": "integer"
					}
				}
			},
			"Folder": {
				"type": "object",
				"required": [
					"id"
				],
				"properties": {
					"id": {
						"type": "string"
					},
					"name": {
						"type": "string"
					},
					"color": {
						"type": [
							"string",
							"null"
						]
					},
					"createdAt": {
						"type": "string",
						"format": "date-time"
					},
					"updatedAt": {
						"type": "string",
						"format": "date-time"
					}
				}
			},
			"FolderList": {
				"type": "object",
				"required": [
					"data"
				],
				"properties": {
					"data": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/Folder"
						}
					}
				}
			},
			"CreateFolderRequest": {
				"type": "object",
				"required": [
					"name"
				],
				"properties": {
					"name": {
						"type": "string",
						"maxLength": 200
					},
					"color": {
						"type": "string",
						"maxLength": 50
					}
				}
			},
			"UpdateFolderRequest": {
				"type": "object",
				"minProperties": 1,
				"properties": {
					"name": {
						"type": "string",
						"maxLength": 200
					},
					"color": {
						"type": "string",
						"maxLength": 50
					}
				}
			},
			"CreateUploadRequest": {
				"type": "object",
				"required": [
					"fileName",
					"contentType"
				],
				"properties": {
					"fileName": {
						"type": "string",
						"maxLength": 500
					},
					"contentType": {
						"type": "string",
						"description": "MIME type of the file. Audio and video formats are accepted."
					},
					"fileSize": {
						"type": "integer",
						"description": "Size in bytes. Maximum 5GB."
					},
					"styleId": {
						"type": "string",
						"enum": [
							"note",
							"transcript",
							"bulletpoints",
							"meeting-notes"
						],
						"default": "note"
					},
					"language": {
						"type": "string",
						"description": "Language code, or omit to detect it."
					}
				}
			},
			"Upload": {
				"type": "object",
				"properties": {
					"noteId": {
						"type": "string"
					},
					"uploadUrl": {
						"type": "string",
						"format": "uri"
					},
					"method": {
						"type": "string",
						"enum": [
							"PUT"
						]
					},
					"headers": {
						"type": "object",
						"additionalProperties": {
							"type": "string"
						}
					},
					"filePath": {
						"type": "string"
					},
					"contentType": {
						"type": "string"
					},
					"expiresAt": {
						"type": "string",
						"format": "date-time"
					},
					"next": {
						"type": "string"
					},
					"notice": {
						"type": "string",
						"description": "Present on sandbox keys."
					}
				}
			},
			"UploadAccepted": {
				"type": "object",
				"properties": {
					"noteId": {
						"type": "string"
					},
					"status": {
						"type": "string"
					},
					"creditsCharged": {
						"type": "integer"
					},
					"durationSeconds": {
						"type": [
							"number",
							"null"
						]
					},
					"poll": {
						"type": "string"
					},
					"alreadyCompleted": {
						"type": "boolean"
					}
				}
			},
			"TranscriptionRequest": {
				"type": "object",
				"required": [
					"audioUrl"
				],
				"properties": {
					"audioUrl": {
						"type": "string",
						"format": "uri",
						"description": "Publicly reachable https URL."
					},
					"language": {
						"type": "string",
						"description": "Language code, or auto."
					},
					"timeoutSeconds": {
						"type": "integer",
						"minimum": 30,
						"maximum": 1800,
						"default": 300
					}
				}
			},
			"TranscriptionResult": {
				"type": "object",
				"properties": {
					"transcript": {
						"type": "string"
					},
					"language": {
						"type": "string"
					},
					"durationSeconds": {
						"type": [
							"number",
							"null"
						]
					},
					"creditsCharged": {
						"type": "integer"
					},
					"notice": {
						"type": "string"
					}
				}
			},
			"SummaryRequest": {
				"type": "object",
				"required": [
					"text"
				],
				"properties": {
					"text": {
						"type": "string",
						"maxLength": 500000
					},
					"styleId": {
						"type": "string",
						"enum": [
							"note",
							"transcript",
							"bulletpoints",
							"meeting-notes"
						],
						"default": "note"
					}
				}
			},
			"SummaryResult": {
				"type": "object",
				"properties": {
					"title": {
						"type": "string"
					},
					"summary": {
						"type": "string"
					},
					"styleId": {
						"type": "string"
					},
					"structuredContent": {
						"type": [
							"object",
							"null"
						],
						"additionalProperties": true
					},
					"creditsCharged": {
						"type": "integer"
					},
					"notice": {
						"type": "string"
					}
				}
			},
			"YoutubeSummaryRequest": {
				"type": "object",
				"required": [
					"youtubeUrl"
				],
				"properties": {
					"youtubeUrl": {
						"type": "string",
						"format": "uri"
					},
					"styleId": {
						"type": "string",
						"enum": [
							"note",
							"transcript",
							"bulletpoints",
							"meeting-notes"
						],
						"default": "note"
					}
				}
			},
			"YoutubeSummaryResult": {
				"allOf": [
					{
						"$ref": "#/components/schemas/SummaryResult"
					},
					{
						"type": "object",
						"properties": {
							"youtubeUrl": {
								"type": "string",
								"format": "uri"
							},
							"durationSeconds": {
								"type": [
									"number",
									"null"
								]
							}
						}
					}
				]
			},
			"Usage": {
				"type": "object",
				"properties": {
					"balance": {
						"type": "object",
						"properties": {
							"plan": {
								"type": "string",
								"enum": [
									"free",
									"pro"
								]
							},
							"grantTotal": {
								"type": "integer"
							},
							"grantRemaining": {
								"type": "integer"
							},
							"purchasedRemaining": {
								"type": "integer"
							},
							"totalRemaining": {
								"type": "integer"
							},
							"grantResetsAt": {
								"type": "string",
								"format": "date-time"
							}
						}
					},
					"spentThisPeriod": {
						"type": "integer"
					},
					"recent": {
						"type": "array",
						"items": {
							"type": "object",
							"properties": {
								"id": {
									"type": "string"
								},
								"operation": {
									"type": "string"
								},
								"credits": {
									"type": "integer"
								},
								"source": {
									"type": "string",
									"enum": [
										"api_key",
										"oauth",
										"mcp"
									]
								},
								"noteId": {
									"type": [
										"string",
										"null"
									]
								},
								"createdAt": {
									"type": "string",
									"format": "date-time"
								}
							}
						}
					},
					"costs": {
						"type": "object",
						"additionalProperties": {
							"type": "integer"
						}
					},
					"topUp": {
						"type": "object",
						"additionalProperties": true
					}
				}
			}
		},
		"responses": {
			"InvalidRequest": {
				"description": "The request was malformed or failed validation.",
				"content": {
					"application/json": {
						"schema": {
							"$ref": "#/components/schemas/Error"
						},
						"example": {
							"error": {
								"type": "invalid_request_error",
								"code": "invalid_request",
								"message": "The request was malformed or failed validation.",
								"doc_url": "https://speaknotes.io/developers/errors#invalid_request"
							}
						}
					}
				}
			},
			"InvalidToken": {
				"description": "The credential is missing, malformed, expired, or revoked.",
				"content": {
					"application/json": {
						"schema": {
							"$ref": "#/components/schemas/Error"
						},
						"example": {
							"error": {
								"type": "invalid_request_error",
								"code": "invalid_token",
								"message": "The credential is missing, malformed, expired, or revoked.",
								"doc_url": "https://speaknotes.io/developers/errors#invalid_token"
							}
						}
					}
				}
			},
			"InsufficientScope": {
				"description": "The credential is valid but lacks the scope this operation needs.",
				"content": {
					"application/json": {
						"schema": {
							"$ref": "#/components/schemas/Error"
						},
						"example": {
							"error": {
								"type": "invalid_request_error",
								"code": "insufficient_scope",
								"message": "The credential is valid but lacks the scope this operation needs.",
								"doc_url": "https://speaknotes.io/developers/errors#insufficient_scope"
							}
						}
					}
				}
			},
			"NotFound": {
				"description": "The requested resource does not exist.",
				"content": {
					"application/json": {
						"schema": {
							"$ref": "#/components/schemas/Error"
						},
						"example": {
							"error": {
								"type": "invalid_request_error",
								"code": "not_found",
								"message": "The requested resource does not exist.",
								"doc_url": "https://speaknotes.io/developers/errors#not_found"
							}
						}
					}
				}
			},
			"Conflict": {
				"description": "The request conflicts with the current state of the resource.",
				"content": {
					"application/json": {
						"schema": {
							"$ref": "#/components/schemas/Error"
						},
						"example": {
							"error": {
								"type": "invalid_request_error",
								"code": "conflict",
								"message": "The request conflicts with the current state of the resource.",
								"doc_url": "https://speaknotes.io/developers/errors#conflict"
							}
						}
					}
				}
			},
			"PayloadTooLarge": {
				"description": "The uploaded file exceeds the size limit.",
				"content": {
					"application/json": {
						"schema": {
							"$ref": "#/components/schemas/Error"
						},
						"example": {
							"error": {
								"type": "invalid_request_error",
								"code": "payload_too_large",
								"message": "The uploaded file exceeds the size limit.",
								"doc_url": "https://speaknotes.io/developers/errors#payload_too_large"
							}
						}
					}
				}
			},
			"UnsupportedMediaType": {
				"description": "The file format is not supported.",
				"content": {
					"application/json": {
						"schema": {
							"$ref": "#/components/schemas/Error"
						},
						"example": {
							"error": {
								"type": "invalid_request_error",
								"code": "unsupported_media_type",
								"message": "The file format is not supported.",
								"doc_url": "https://speaknotes.io/developers/errors#unsupported_media_type"
							}
						}
					}
				}
			},
			"InsufficientCredits": {
				"description": "The account has no credits left for this operation.",
				"content": {
					"application/json": {
						"schema": {
							"$ref": "#/components/schemas/Error"
						},
						"example": {
							"error": {
								"type": "invalid_request_error",
								"code": "insufficient_credits",
								"message": "The account has no credits left for this operation.",
								"doc_url": "https://speaknotes.io/developers/errors#insufficient_credits"
							}
						}
					}
				}
			},
			"RateLimited": {
				"description": "Too many requests. Retry after the interval in the Retry-After header.",
				"content": {
					"application/json": {
						"schema": {
							"$ref": "#/components/schemas/Error"
						},
						"example": {
							"error": {
								"type": "invalid_request_error",
								"code": "rate_limited",
								"message": "Too many requests. Retry after the interval in the Retry-After header.",
								"doc_url": "https://speaknotes.io/developers/errors#rate_limited"
							}
						}
					}
				}
			},
			"UpstreamFailure": {
				"description": "A provider SpeakNotes depends on failed.",
				"content": {
					"application/json": {
						"schema": {
							"$ref": "#/components/schemas/Error"
						},
						"example": {
							"error": {
								"type": "invalid_request_error",
								"code": "upstream_failure",
								"message": "A provider SpeakNotes depends on failed.",
								"doc_url": "https://speaknotes.io/developers/errors#upstream_failure"
							}
						}
					}
				}
			},
			"ContentPolicy": {
				"description": "The content was refused by the AI content policy.",
				"content": {
					"application/json": {
						"schema": {
							"$ref": "#/components/schemas/Error"
						},
						"example": {
							"error": {
								"type": "invalid_request_error",
								"code": "content_policy",
								"message": "The content was refused by the AI content policy.",
								"doc_url": "https://speaknotes.io/developers/errors#content_policy"
							}
						}
					}
				}
			},
			"InternalError": {
				"description": "SpeakNotes failed to process the request.",
				"content": {
					"application/json": {
						"schema": {
							"$ref": "#/components/schemas/Error"
						},
						"example": {
							"error": {
								"type": "invalid_request_error",
								"code": "internal_error",
								"message": "SpeakNotes failed to process the request.",
								"doc_url": "https://speaknotes.io/developers/errors#internal_error"
							}
						}
					}
				}
			}
		}
	},
	"x-speaknotes": {
		"mcpEndpoint": "https://api.speaknotes.io/mcp",
		"mcpManifest": "https://speaknotes.io/.well-known/mcp.json",
		"sandboxKeyPrefix": "sn_test_",
		"monthlyCreditGrants": {
			"free": 60,
			"pro": 2000
		},
		"creditCosts": {
			"transcriptionPerMinute": 1,
			"youtubePerMinute": 1,
			"summarizeText": 2,
			"askQuestion": 1,
			"documentFlat": 2
		},
		"rateLimits": {
			"read": {
				"requests": 120,
				"windowSeconds": 60
			},
			"write": {
				"requests": 30,
				"windowSeconds": 60
			},
			"processing": {
				"requests": 10,
				"windowSeconds": 60
			}
		},
		"errorStatuses": {
			"invalid_request": 400,
			"invalid_token": 401,
			"insufficient_scope": 403,
			"not_found": 404,
			"method_not_allowed": 405,
			"conflict": 409,
			"payload_too_large": 413,
			"unsupported_media_type": 415,
			"insufficient_credits": 402,
			"rate_limited": 429,
			"upstream_failure": 502,
			"content_policy": 422,
			"internal_error": 500
		}
	}
}
