{"openapi":"3.1.0","info":{"title":"Cogram API","description":"\nAPI for integrating with Cogram. Every key is scoped to one organization.\n\n- **Auth**: `Authorization: Bearer <api-key>`. Admins mint keys in Organization\n  Settings → Integrations → API Keys.\n- **Pagination**: `page` (1-indexed) + `page_size` (default 50, max 100); returns\n  `{\"data\": [...], \"total\": N, \"page\": N, \"page_size\": N}`. `total` = all matches.\n- **Errors**: `{\"error\": \"<code>\", \"message\": \"<text>\"}`. Branch on `error`; `message`\n  wording may change.\n- **Rate limit**: per key, 1000 req/min by default. `429` carries `Retry-After`,\n  `X-RateLimit-Limit`, `X-RateLimit-Remaining`.\n- **Attribution**: optional `X-Forwarded-User` header (≤200 chars) names the end user\n  behind the call in Cogram's audit trail.\n\nSee <a href=\"https://docs.cogram.com/administration/cogram-api\"\ntarget=\"_blank\">API documentation</a> for guides and examples.\n","version":"v1"},"paths":{"/action-items":{"get":{"tags":["action-items"],"summary":"List action items","description":"List action items in the organization.\n\nReturns a paginated list of action items belonging to the organization.\nBy default, only non-archived items are returned.\n\nUse `project_id` to filter items from meetings linked to a specific project.\nNote: action items from meetings not linked to any project are excluded\nwhen filtering by project_id.\n\nUse `q` to search descriptions and `assignee` for partial name matching\n(both case-insensitive).\n\nSorting by nullable fields (`due_date`, `assignee`, `status`, `description`)\nalways places items with no value at the bottom, regardless of sort direction.\n\nUnknown `project_id` or `meeting_id` values return an empty list, not 404.","operationId":"external_list_action_items_route_action_items_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (1-indexed).","default":1,"title":"Page"},"description":"Page number (1-indexed)."},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"title":"ActionItemStatus","enum":["SUGGESTED","ACCEPTED","REJECTED","COMPLETED"],"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"include_archived","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Archived"}},{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Matches on the source meeting's project. Items from meetings with no project are excluded.","title":"Project Id"},"description":"Matches on the source meeting's project. Items from meetings with no project are excluded."},{"name":"meeting_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Meeting Id"}},{"name":"assignee","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"description":"Case-insensitive partial match on assignee name.","title":"Assignee"},"description":"Case-insensitive partial match on assignee name."},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"description":"Case-insensitive search in `description`.","title":"Q"},"description":"Case-insensitive search in `description`."},{"name":"sort_by","in":"query","required":false,"schema":{"$ref":"#/components/schemas/ActionItemSortBy","default":"created_at"}},{"name":"sort_dir","in":"query","required":false,"schema":{"enum":["asc","desc"],"type":"string","default":"asc","title":"Sort Dir"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionItemListV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/audit-log":{"get":{"tags":["audit-log"],"summary":"List audit log entries","description":"List what Cogram removed from the organization in a time window.\n\nEach entry is one email or meeting that was deleted, or whose content was\nredacted, with the reason and the user who triggered it. Entries never\ncarry content: an email is identified by its `internet_message_id`.\n\nThe window is half-open, `[occurred_from, occurred_to)`, so consecutive\nwindows neither overlap nor leave a gap. It may span at most 366 days; a\nlonger window returns 400. Entries are ordered by `occurred_at`, then `id`.\n\nAn entry's `occurred_at` is when it was written; it becomes visible when\nits deleting transaction commits, which can be later. Query windows that\nended at least 1 hour ago: then consecutive windows read every entry once.","operationId":"external_list_audit_log_route_audit_log_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"occurred_from","in":"query","required":true,"schema":{"type":"string","format":"date-time","description":"Inclusive lower bound on `occurred_at`. Must include a UTC offset (e.g. 2026-10-20T00:00:00Z). The window may span at most 366 days.","title":"Occurred From"},"description":"Inclusive lower bound on `occurred_at`. Must include a UTC offset (e.g. 2026-10-20T00:00:00Z). The window may span at most 366 days."},{"name":"occurred_to","in":"query","required":true,"schema":{"type":"string","format":"date-time","description":"Exclusive upper bound on `occurred_at`. Must include a UTC offset. The window may span at most 366 days.","title":"Occurred To"},"description":"Exclusive upper bound on `occurred_at`. Must include a UTC offset. The window may span at most 366 days."},{"name":"entity_type","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/AuditEntityType"},{"type":"null"}],"title":"Entity Type"}},{"name":"action","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/AuditAction"},{"type":"null"}],"title":"Action"}},{"name":"reason","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/AuditReason"},{"type":"null"}],"title":"Reason"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (1-indexed).","default":1,"title":"Page"},"description":"Page number (1-indexed)."},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditLogListV1"}}}},"400":{"description":"Bad request","content":{"application/json":{"example":{"error":"bad_request","message":"Invalid request"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/backup-runs":{"post":{"tags":["backup-runs"],"summary":"Report a backup run","description":"Record that a backup run started or finished.\n\nReport `started` when a run begins and `finished` with a `status` when it\nends. A `finished` run with `status: success` clears any outstanding\nstale-backup alert, so the next outage produces a fresh notification.","operationId":"external_create_backup_run_route_backup_runs_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackupRunCreateV1"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackupRunV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorV1"},"example":{"error":"api_key_invalid","message":"Invalid API key"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorV1"},"example":{"error":"not_found","message":"Resource not found"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorV1"},"example":{"error":"validation_error","message":"body -> name: Field required"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorV1"},"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorV1"},"example":{"error":"internal_error","message":"An internal error occurred"}}}}},"security":[{"HTTPBearer":[]}]}},"/board-items":{"get":{"tags":["board-items"],"summary":"List board items","description":"List kanban board items in the organization.\n\nReturns a paginated list across every project in the organization.\nBy default, only non-archived items are included.\n\nUse `project_id` to scope to a single project, `meeting_id` to find\nitems originated by a meeting, or `meeting_series_uid` to correlate\nitems tied to the same recurring meeting series.\n\nUse `q` to search `title` and `body` and `assignee` for partial\nname matching (both case-insensitive).\n\nSorting by nullable fields (`assignee`, `last_status_changed_at`)\nalways places items with no value at the bottom, regardless of sort\ndirection.\n\nUnknown `project_id` or `meeting_id` values return an empty list, not 404.","operationId":"external_list_board_items_route_board_items_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (1-indexed).","default":1,"title":"Page"},"description":"Page number (1-indexed)."},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"title":"BoardItemStatus","enum":["TODO","IN_PROGRESS","DONE","DISMISSED"],"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"include_archived","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Archived"}},{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id"}},{"name":"meeting_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter on `source_meeting_id` — items this meeting created.","title":"Meeting Id"},"description":"Filter on `source_meeting_id` — items this meeting created."},{"name":"meeting_series_uid","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Meeting Series Uid"}},{"name":"assignee","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"description":"Case-insensitive partial match on assignee name.","title":"Assignee"},"description":"Case-insensitive partial match on assignee name."},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"description":"Case-insensitive search in `title` and `body`.","title":"Q"},"description":"Case-insensitive search in `title` and `body`."},{"name":"sort_by","in":"query","required":false,"schema":{"$ref":"#/components/schemas/BoardItemSortBy","default":"created_at"}},{"name":"sort_dir","in":"query","required":false,"schema":{"enum":["asc","desc"],"type":"string","default":"asc","title":"Sort Dir"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardItemListV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/board-items/{item_id}":{"get":{"tags":["board-items"],"summary":"Get board item","description":"Get a single board item by id, with its full history timeline.\n\n`history` is ordered oldest-first. Each entry may represent a status\ntransition, an assignee change, a comment, or any combination — see\nthe schema reference for details on how to distinguish the cases.\n\nReturns 404 if the item doesn't exist or belongs to another organization.","operationId":"external_get_board_item_route_board_items__item_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"item_id","in":"path","required":true,"schema":{"type":"string","title":"Item Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardItemDetailV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/project-types":{"get":{"tags":["project-types"],"summary":"List project types","description":"List project types in the organization.\n\nReturns a paginated list of project types along with the number of\nprojects currently assigned to each type.","operationId":"external_list_project_types_route_project_types_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (1-indexed).","default":1,"title":"Page"},"description":"Page number (1-indexed)."},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectTypeListV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}},"post":{"tags":["project-types"],"summary":"Create project type","description":"Create a new project type.\n\nProject type names must be unique within the organization\n(case-insensitive).","operationId":"external_create_project_type_route_project_types_post","security":[{"HTTPBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectTypeCreateV1"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectTypeV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"409":{"description":"Conflict","content":{"application/json":{"example":{"error":"conflict","message":"Resource already exists"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/project-types/{type_id}":{"get":{"tags":["project-types"],"summary":"Get project type","description":"Get a project type by ID.","operationId":"external_get_project_type_route_project_types__type_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"type_id","in":"path","required":true,"schema":{"type":"string","title":"Type Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectTypeV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}},"patch":{"tags":["project-types"],"summary":"Update project type","description":"Update a project type.\n\nOnly provided fields are updated.","operationId":"external_update_project_type_route_project_types__type_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"type_id","in":"path","required":true,"schema":{"type":"string","title":"Type Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectTypeUpdateV1"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectTypeV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"409":{"description":"Conflict","content":{"application/json":{"example":{"error":"conflict","message":"Resource already exists"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}},"delete":{"tags":["project-types"],"summary":"Delete project type","description":"Delete a project type.\n\nProjects currently using this type will have their type cleared\n(foreign key SET NULL).","operationId":"external_delete_project_type_route_project_types__type_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"type_id","in":"path","required":true,"schema":{"type":"string","title":"Type Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/project-templates":{"get":{"tags":["project-templates"],"summary":"List project templates","description":"List project templates in the organization, ordered by name.","operationId":"external_list_project_templates_route_project_templates_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (1-indexed).","default":1,"title":"Page"},"description":"Page number (1-indexed)."},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectTemplateListV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"403":{"description":"Forbidden","content":{"application/json":{"example":{"error":"forbidden","message":"The API key's creator is not allowed to do this"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}},"post":{"tags":["project-templates"],"summary":"Create project template","description":"Create a project template. Requires an org admin or owner key creator.","operationId":"external_create_project_template_route_project_templates_post","security":[{"HTTPBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectTemplateWriteV1"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectTemplateV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"403":{"description":"Forbidden","content":{"application/json":{"example":{"error":"forbidden","message":"The API key's creator is not allowed to do this"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"409":{"description":"Conflict","content":{"application/json":{"example":{"error":"conflict","message":"Resource already exists"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/project-templates/{template_id}":{"get":{"tags":["project-templates"],"summary":"Get project template","description":"Get a project template with its group bindings.","operationId":"external_get_project_template_route_project_templates__template_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectTemplateV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"403":{"description":"Forbidden","content":{"application/json":{"example":{"error":"forbidden","message":"The API key's creator is not allowed to do this"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}},"put":{"tags":["project-templates"],"summary":"Replace project template","description":"Replace every field and group binding of a project template.\n\nProjects already created from the template are not changed. Requires an\norg admin or owner key creator.","operationId":"external_update_project_template_route_project_templates__template_id__put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectTemplateWriteV1"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectTemplateV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"403":{"description":"Forbidden","content":{"application/json":{"example":{"error":"forbidden","message":"The API key's creator is not allowed to do this"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"409":{"description":"Conflict","content":{"application/json":{"example":{"error":"conflict","message":"Resource already exists"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/projects":{"get":{"tags":["projects"],"summary":"List projects","description":"List projects in the organization.\n\nReturns a paginated list of projects. By default, only active (non-archived)\nprojects are returned. Set include_archived=true to include archived projects.\n\nPass ``include=counts`` to populate ``related_counts`` on every project in\nthe page. The counts for the whole page cost one extra query regardless of\n``page_size``.\n\nPass ``include=members`` to populate ``members`` on every project in the\npage. Also one extra query for the whole page, but unlike counts the\nresponse grows with the rosters it carries.\n\nPass ``updated_since`` to poll for change instead of reading the whole\norganization. It answers \"what changed\", not \"what changed to what\": the\nresponse carries each project's current state, so a caller tracking a field\n— an archive flip, a rename, a Unanet custom field — compares it against\nwhat it last saw. Two properties make that safe to build on:\n\n- **It also reports creations.** A project created inside the window has no\n  ``updated_at`` yet, and is matched on ``created_at`` instead. One cursor\n  therefore covers both, and a caller needs no second poll for new projects.\n- **The bound is inclusive and the order is oldest change first.** Store the\n  newest ``updated_at`` (or ``created_at``) you were served and pass it back;\n  you may be re-served that one row, and you will not miss one that shares\n  its timestamp across a page boundary.\n\nIt does not report **deletions** — a deleted project is simply absent from\nlater responses. Archiving is not a deletion: an archived project is still\nlisted under ``include_archived=true``, with ``is_active: false``.","operationId":"external_list_projects_route_projects_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"include","in":"query","required":false,"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectIncludeV1"},"description":"Optional response sections. `counts` populates `related_counts`; `members` populates `members`. Omitted sections come back as null. Repeat the param (`include=counts&include=members`) or comma-separate the values (`include=counts,members`) — both work.","title":"Include"},"description":"Optional response sections. `counts` populates `related_counts`; `members` populates `members`. Omitted sections come back as null. Repeat the param (`include=counts&include=members`) or comma-separate the values (`include=counts,members`) — both work."},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (1-indexed).","default":1,"title":"Page"},"description":"Page number (1-indexed)."},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}},{"name":"include_archived","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Archived"}},{"name":"updated_since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Only return projects created or changed at or after this instant, oldest change first. Must include a UTC offset (e.g. 2026-08-17T18:03:11Z). Compares against `updated_at`, falling back to `created_at` for a project that has never been updated.","title":"Updated Since"},"description":"Only return projects created or changed at or after this instant, oldest change first. Must include a UTC offset (e.g. 2026-08-17T18:03:11Z). Compares against `updated_at`, falling back to `created_at` for a project that has never been updated."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectListV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}},"post":{"tags":["projects"],"summary":"Create project","description":"Create a new project.\n\nCreates a project in the organization associated with the API key.\nProject names must be unique within the organization. With `template_id`,\nthe project also gets the template's project type and groups.","operationId":"external_create_project_route_projects_post","security":[{"HTTPBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectCreateV1"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"403":{"description":"Forbidden","content":{"application/json":{"example":{"error":"forbidden","message":"The API key's creator is not allowed to do this"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"409":{"description":"Conflict","content":{"application/json":{"example":{"error":"conflict","message":"Resource already exists"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/projects/{project_id}":{"get":{"tags":["projects"],"summary":"Get project","description":"Get a project by ID.\n\nReturns the project details if found and belongs to the organization.\n\nPass ``include=counts`` to populate ``related_counts`` with how many\nemails, documents, drawings, submittals, and so on are filed under the\nproject. Pass ``include=members`` to populate ``members`` with the users\nholding a role on it. Both are omitted by default, so the response stays a\nsingle-row read.","operationId":"external_get_project_route_projects__project_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"include","in":"query","required":false,"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectIncludeV1"},"description":"Optional response sections. `counts` populates `related_counts`; `members` populates `members`. Omitted sections come back as null. Repeat the param (`include=counts&include=members`) or comma-separate the values (`include=counts,members`) — both work.","title":"Include"},"description":"Optional response sections. `counts` populates `related_counts`; `members` populates `members`. Omitted sections come back as null. Repeat the param (`include=counts&include=members`) or comma-separate the values (`include=counts,members`) — both work."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}},"patch":{"tags":["projects"],"summary":"Update project","description":"Update a project.\n\nUpdates the specified fields of the project. Only provided fields are updated.\nSet is_active=false to archive the project.\n\nNote: is_public bypasses the org allow_override lock — API keys are\nadmin-scoped credentials, so the key holder is trusted to set any value.","operationId":"external_update_project_route_projects__project_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectUpdateV1"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"409":{"description":"Conflict","content":{"application/json":{"example":{"error":"conflict","message":"Resource already exists"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}},"delete":{"tags":["projects"],"summary":"Delete project","description":"Delete (archive) a project.\n\nSoft-deletes the project by setting archived_at. The project can still\nbe retrieved using include_archived=true but is considered inactive.","operationId":"external_delete_project_route_projects__project_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/projects/{project_id}/members":{"get":{"tags":["projects"],"summary":"List project members","description":"List members of a project.\n\nReturns all non-banned users who are members of the project along with\ntheir roles (OWNER, MEMBER, or VIEWER).","operationId":"external_list_project_members_route_projects__project_id__members_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectMemberV1"},"title":"Response External List Project Members Route Projects  Project Id  Members Get"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/projects/{project_id}/members/{user_id}":{"put":{"tags":["projects"],"summary":"Add or update project member","description":"Add or update a project member.\n\nIf the user is not a member, they will be added with the specified role.\nIf the user is already a member, their role will be updated.\nThe user must exist in the same organization as the API key.","operationId":"external_set_project_member_route_projects__project_id__members__user_id__put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectMemberUpdateV1"}}}},"responses":{"204":{"description":"Successful Response"},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}},"delete":{"tags":["projects"],"summary":"Remove project member","description":"Remove a member from a project.\n\nRemoves the user's project role. Returns 404 if the user was not a member.","operationId":"external_remove_project_member_route_projects__project_id__members__user_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/data-exports":{"post":{"tags":["data-exports"],"summary":"Create data_export","description":"Create a new data_export in PENDING and enqueue the build worker.\n\nPass ``project_ids`` to scope the export to those projects (list\ncandidates via ``GET /v1/projects``); omit it for an org-wide export,\nwhich is capped at a 6-month range. Any project the API key's org\ndoesn't own returns 404 — same as an unknown one, and the whole\nrequest is rejected rather than exporting the subset that resolved.\n\nThe build is asynchronous — poll ``GET /v1/data-exports/{id}`` for status\nand (once ``status == \"completed\"``) freshly-signed download URLs\nfor each part.","operationId":"external_create_data_export_route_data_exports_post","security":[{"HTTPBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataExportCreateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataExportResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"example":{"error":"bad_request","message":"Invalid request"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"409":{"description":"Conflict","content":{"application/json":{"example":{"error":"conflict","message":"Resource already exists"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}},"get":{"tags":["data-exports"],"summary":"List data_exports","description":"List every data_export for the org, newest first.\n\nPaginated per the external-API contract ``{data, total, page,\npage_size}``. Defaults: page=1, page_size=50, max page_size=100.\n\nIncludes terminal rows (``completed``, ``failed``, ``cancelled``,\n``expired``) — they're the audit trail. ``parts`` (with signed URLs)\nis populated only on rows in ``completed`` status; signed URLs\nexpire one hour after generation and are regenerated on every read.","operationId":"external_list_data_exports_route_data_exports_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (1-indexed).","default":1,"title":"Page"},"description":"Page number (1-indexed)."},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataExportListV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/data-exports/{data_export_id}":{"get":{"tags":["data-exports"],"summary":"Get data_export","description":"Get one data_export's status and (when ``status == \"completed\"``)\nfreshly-signed download URLs for each part.\n\nURLs are regenerated on every read so clients never accumulate stale\nones. Cross-org data_export ids return 404 — the API key only sees its\nown org's data_exports.","operationId":"external_get_data_export_route_data_exports__data_export_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"data_export_id","in":"path","required":true,"schema":{"type":"string","title":"Data Export Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataExportResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/data-exports/{data_export_id}/cancel":{"post":{"tags":["data-exports"],"summary":"Cancel data_export","description":"Cancel a ``pending`` or ``running`` data_export.\n\n``pending`` short-circuits straight to ``cancelled`` — no in-flight\nbuild work to interrupt. ``running`` transitions to ``cancelling``;\nthe worker (E-1454) picks up the signal between zip parts and the\nnext status read will reflect the terminal state. Terminal statuses\nreturn 409 — there's no in-flight work to stop.","operationId":"external_cancel_data_export_route_data_exports__data_export_id__cancel_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"data_export_id","in":"path","required":true,"schema":{"type":"string","title":"Data Export Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataExportResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"409":{"description":"Conflict","content":{"application/json":{"example":{"error":"conflict","message":"Resource already exists"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/directory/contact-company-links":{"get":{"tags":["directory"],"summary":"List proposed contact-to-company links","description":"List the contacts that have no company but whose email domain a company owns.\n\nNothing is written. Each entry names the company that `POST` would link and,\nwhere a domain sits on more than one company, the ones that lost to it.\n\nA contact whose domain no company claims is not listed: there is nothing to\npropose. Archived contacts are excluded, and an archived company claims no\ndomain.","operationId":"external_list_contact_company_links_route_directory_contact_company_links_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (1-indexed).","default":1,"title":"Page"},"description":"Page number (1-indexed)."},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactCompanyLinkListV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}},"post":{"tags":["directory"],"summary":"Apply proposed contact-to-company links","description":"Set the company on contacts that have none, from their email domain.\n\nApplies the proposal `GET /directory/contact-company-links` returns, or the\npart of it named by `contact_ids`. Read that first: a company carrying a\nconsumer domain such as `gmail.com` claims every personal address in the\norganization, and this endpoint cannot tell that apart from a real firm.\n\nThe proposal is recomputed here rather than carried over from the `GET`, so\n`contact_ids` chooses contacts, not companies: if the company that won a\ncontested domain is archived in between, the runner-up wins this call. The\n`applied` list names the company each contact actually got, so compare it\nagainst what you reviewed.\n\nKeep the `applied` list the response returns. It names every contact this\ncall gave a company to, and nothing else records which contacts had none\nbeforehand — so it is what a bad run has to be reversed from.\n\nA contact that already has a company is never touched, whether it had one\nall along or gained one between the read and this call. Safe to repeat —\na second call with nothing left to do links nothing.","operationId":"external_apply_contact_company_links_route_directory_contact_company_links_post","security":[{"HTTPBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactCompanyLinkApplyV1"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactCompanyLinkApplyResultV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/users":{"get":{"tags":["users"],"summary":"List users","description":"List users.\n\nReturns a paginated list of users in the organization.","operationId":"external_list_users_route_users_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (1-indexed).","default":1,"title":"Page"},"description":"Page number (1-indexed)."},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserListV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}},"/users/{user_id}":{"patch":{"tags":["users"],"summary":"Update user","description":"Update a user.\n\nCan only modify users whose current roles are MEMBER or ADMIN.\nCan assign only MEMBER and ADMIN roles.","operationId":"external_update_user_route_users__user_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUpdateV1"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserV1"}}}},"400":{"description":"Bad request","content":{"application/json":{"example":{"error":"bad_request","message":"Invalid request"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"api_key_invalid","message":"Invalid API key"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"404":{"description":"Not found","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"422":{"description":"Validation error","content":{"application/json":{"example":{"error":"validation_error","message":"body -> name: Field required"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Rate limit exceeded. Please try again later."},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"internal_error","message":"An internal error occurred"},"schema":{"$ref":"#/components/schemas/ErrorV1"}}}}}}}},"components":{"schemas":{"ActionItemListV1":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ActionItemV1"},"type":"array","title":"Data"},"total":{"type":"integer","title":"Total","description":"Total matches across all pages, not just this page."},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"}},"type":"object","required":["data","total","page","page_size"],"title":"ActionItemListV1","description":"Paginated action item list response."},"ActionItemSortBy":{"type":"string","enum":["created_at","due_date","status","assignee","description"],"title":"ActionItemSortBy","description":"Fields that action items can be sorted by."},"ActionItemV1":{"properties":{"id":{"type":"string","title":"Id"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"assignee":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Assignee","description":"Free-text name, not a user reference."},"status":{"anyOf":[{"type":"string","enum":["SUGGESTED","ACCEPTED","REJECTED","COMPLETED"],"title":"ActionItemStatus"},{"type":"null"}],"title":"Status"},"origin":{"anyOf":[{"type":"string","enum":["AUTO","MANUAL"],"title":"ActionItemOrigin"},{"type":"null"}],"title":"Origin","description":"`AUTO` (AI-extracted) or `MANUAL` (user-created)."},"due_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due Date"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"archived":{"type":"boolean","title":"Archived"},"meeting_id":{"type":"string","title":"Meeting Id"},"meeting_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Meeting Name"},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id","description":"Project the source meeting is filed under."},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At","description":"Null if never updated."}},"type":"object","required":["id","description","assignee","status","origin","due_date","completed_at","archived","meeting_id","meeting_name","project_id","created_at","updated_at"],"title":"ActionItemV1","description":"Action item response."},"AllTimeFilter":{"properties":{"mode":{"type":"string","const":"all_time","title":"Mode","default":"all_time"}},"type":"object","title":"AllTimeFilter","description":"All available data, no time bound. Only valid for project-scoped\ndata_exports (one or more ``project_ids``)."},"AppliedLinkV1":{"properties":{"contact_id":{"type":"string","title":"Contact Id"},"company_id":{"type":"string","title":"Company Id"}},"type":"object","required":["contact_id","company_id"],"title":"AppliedLinkV1","description":"One link this call wrote."},"AssignableUserRoleV1":{"type":"string","enum":["MEMBER","ADMIN"],"title":"AssignableUserRoleV1","description":"Roles that can be assigned via the External API."},"AuditAction":{"type":"string","enum":["DELETED","CONTENT_REDACTED"],"title":"AuditAction","description":"What happened to the record."},"AuditEntityType":{"type":"string","enum":["EMAIL","MEETING"],"title":"AuditEntityType","description":"The kind of record an audit row is about."},"AuditLogEntryV1":{"properties":{"id":{"type":"integer","title":"Id","description":"Unique, increasing in the order the entries were written. Breaks ties between entries with the same `occurred_at`."},"organization_id":{"type":"string","title":"Organization Id"},"entity_type":{"$ref":"#/components/schemas/AuditEntityType","description":"The kind of record: `EMAIL` or `MEETING`."},"entity_id":{"type":"string","title":"Entity Id","description":"The record's Cogram id. A deleted email no longer exists; a meeting whose content was redacted still does."},"action":{"$ref":"#/components/schemas/AuditAction","description":"`DELETED`: the record is gone. `CONTENT_REDACTED`: its content is gone, and the record stays as a visible shell."},"reason":{"$ref":"#/components/schemas/AuditReason","description":"Why it was removed."},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id","description":"The project the record was in when it was removed."},"actor_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Actor User Id","description":"The user who triggered the removal; null for a system event."},"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"},"context":{"anyOf":[{"$ref":"#/components/schemas/EmailAuditContextV1"},{"$ref":"#/components/schemas/MeetingAuditContextV1"},{"type":"null"}],"title":"Context","description":"Email rows: the email's identifiers and retention facts. Meeting rows: the raw deletion reason for reason `OTHER`, otherwise null."}},"type":"object","required":["id","organization_id","entity_type","entity_id","action","reason","project_id","actor_user_id","occurred_at","context"],"title":"AuditLogEntryV1","description":"One record, or its content, that Cogram removed."},"AuditLogListV1":{"properties":{"data":{"items":{"$ref":"#/components/schemas/AuditLogEntryV1"},"type":"array","title":"Data"},"total":{"type":"integer","title":"Total","description":"Total matches across all pages, not just this page."},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"}},"type":"object","required":["data","total","page","page_size"],"title":"AuditLogListV1","description":"Paginated audit log response."},"AuditReason":{"type":"string","enum":["USER_DELETE","BULK_UNFILE","DUPLICATE_REMOVED","PROJECT_DELETED","MANUAL_PURGE","RETENTION_EXPIRY","USER_OFFBOARDED","OTHER"],"title":"AuditReason","description":"Why the record or its content was removed."},"BackupRunCreateV1":{"properties":{"event":{"$ref":"#/components/schemas/ConnectorBackupEvent","description":"`started` when a run begins, `finished` when it ends."},"schedule_interval_hours":{"type":"integer","maximum":8760.0,"minimum":1.0,"title":"Schedule Interval Hours","description":"The connector's configured interval between runs. Cogram measures backup health against this rather than a server-side setting, because only the connector knows its own schedule."},"status":{"anyOf":[{"$ref":"#/components/schemas/ConnectorBackupStatus"},{"type":"null"}],"description":"Required on `finished`; must be omitted on `started`."},"projects_synced":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Projects Synced","description":"Projects filed by this run. Zero is a healthy result — a run with no changed projects exports nothing."},"error":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Error","description":"Failure detail. Only meaningful when `status` is `failed`."}},"type":"object","required":["event","schedule_interval_hours"],"title":"BackupRunCreateV1","description":"One backup lifecycle event, reported by the connector."},"BackupRunV1":{"properties":{"id":{"type":"string","title":"Id"},"event":{"$ref":"#/components/schemas/ConnectorBackupEvent"},"status":{"anyOf":[{"$ref":"#/components/schemas/ConnectorBackupStatus"},{"type":"null"}]},"schedule_interval_hours":{"type":"integer","title":"Schedule Interval Hours"},"projects_synced":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Projects Synced"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","event","status","schedule_interval_hours","projects_synced","error","created_at"],"title":"BackupRunV1","description":"A recorded backup lifecycle event."},"BoardItemDetailV1":{"properties":{"id":{"type":"string","title":"Id"},"project_id":{"type":"string","title":"Project Id"},"project_name":{"type":"string","title":"Project Name"},"title":{"type":"string","title":"Title"},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body","description":"Markdown."},"status":{"type":"string","enum":["TODO","IN_PROGRESS","DONE","DISMISSED"],"title":"BoardItemStatus"},"priority":{"anyOf":[{"type":"string","enum":["URGENT","HIGH","NORMAL","LOW"],"title":"BoardItemPriority"},{"type":"null"}],"title":"Priority"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date","description":"Calendar date, no time component."},"assignee":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Assignee","description":"Free-text name; may differ from the linked user's name."},"assignee_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Assignee User Id","description":"Set only when the assignee name matched a Cogram user."},"source_meeting_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Meeting Id"},"source_meeting_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Meeting Name"},"meeting_series_uid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Meeting Series Uid"},"archived":{"type":"boolean","title":"Archived"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At","description":"Null if never updated."},"last_status_changed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Status Changed At","description":"Timestamp of the newest history entry."},"history":{"items":{"$ref":"#/components/schemas/BoardItemHistoryEntryV1"},"type":"array","title":"History","description":"Oldest first."}},"type":"object","required":["id","project_id","project_name","title","body","status","priority","due_date","assignee","assignee_user_id","source_meeting_id","source_meeting_name","meeting_series_uid","archived","created_at","updated_at","last_status_changed_at","history"],"title":"BoardItemDetailV1","description":"Single board item with its full history embedded.\n\n``history`` is ordered oldest-first, matching the internal history\nendpoint so external consumers see the same timeline ordering."},"BoardItemHistoryEntryV1":{"properties":{"id":{"type":"string","title":"Id"},"changed_at":{"type":"string","format":"date-time","title":"Changed At"},"changed_by_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Changed By User Id","description":"Null for automated (LLM) changes, and also for a user-made change whose user has since been deleted. Null plus a set `triggered_by_meeting_id` is what identifies an automated change."},"changed_by_user_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Changed By User Name"},"triggered_by_meeting_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Triggered By Meeting Id"},"triggered_by_meeting_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Triggered By Meeting Name"},"previous_status":{"anyOf":[{"type":"string","enum":["TODO","IN_PROGRESS","DONE","DISMISSED"],"title":"BoardItemStatus"},{"type":"null"}],"title":"Previous Status","description":"Null on the creation entry and on non-status rows."},"new_status":{"anyOf":[{"type":"string","enum":["TODO","IN_PROGRESS","DONE","DISMISSED"],"title":"BoardItemStatus"},{"type":"null"}],"title":"New Status","description":"Null on non-status rows."},"previous_assignee":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Previous Assignee"},"new_assignee":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"New Assignee"},"previous_assignee_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Previous Assignee User Id"},"new_assignee_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"New Assignee User Id"},"previous_priority":{"anyOf":[{"type":"string","enum":["URGENT","HIGH","NORMAL","LOW"],"title":"BoardItemPriority"},{"type":"null"}],"title":"Previous Priority"},"new_priority":{"anyOf":[{"type":"string","enum":["URGENT","HIGH","NORMAL","LOW"],"title":"BoardItemPriority"},{"type":"null"}],"title":"New Priority"},"previous_due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Previous Due Date"},"new_due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"New Due Date"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"LLM-authored; only on reconciliation-triggered rows."},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment","description":"User-authored; null on pure status/assignee rows."}},"type":"object","required":["id","changed_at","changed_by_user_id","changed_by_user_name","triggered_by_meeting_id","triggered_by_meeting_name","previous_status","new_status","previous_assignee","new_assignee","previous_assignee_user_id","new_assignee_user_id","previous_priority","new_priority","previous_due_date","new_due_date","reason","comment"],"title":"BoardItemHistoryEntryV1","description":"A single history entry on a board item.\n\nEach entry is backed by a ``BoardItemHistoryEntry`` row, which may\nrepresent a status transition, an assignee change, a priority shift,\na due-date move, a comment, or any combination thereof. Consumers\ncan distinguish the case by inspecting:\n\n    * ``previous_status != new_status`` — status transition\n    * ``new_assignee != previous_assignee`` (or ``*_user_id`` equivalents)\n      — assignee change\n    * ``new_priority != previous_priority`` — priority change\n    * ``new_due_date != previous_due_date`` — due-date change\n    * ``comment is not None`` and no other delta — pure comment\n\n``changed_by_user_id == None`` combined with a set ``triggered_by_meeting_id``\nindicates the change was applied automatically by LLM-driven reconciliation."},"BoardItemListV1":{"properties":{"data":{"items":{"$ref":"#/components/schemas/BoardItemV1"},"type":"array","title":"Data"},"total":{"type":"integer","title":"Total","description":"Total matches across all pages, not just this page."},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"}},"type":"object","required":["data","total","page","page_size"],"title":"BoardItemListV1","description":"Paginated board item list response."},"BoardItemSortBy":{"type":"string","enum":["created_at","updated_at","last_status_changed_at","status","title","assignee"],"title":"BoardItemSortBy","description":"Fields that board items can be sorted by."},"BoardItemV1":{"properties":{"id":{"type":"string","title":"Id"},"project_id":{"type":"string","title":"Project Id"},"project_name":{"type":"string","title":"Project Name"},"title":{"type":"string","title":"Title"},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body","description":"Markdown."},"status":{"type":"string","enum":["TODO","IN_PROGRESS","DONE","DISMISSED"],"title":"BoardItemStatus"},"priority":{"anyOf":[{"type":"string","enum":["URGENT","HIGH","NORMAL","LOW"],"title":"BoardItemPriority"},{"type":"null"}],"title":"Priority"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date","description":"Calendar date, no time component."},"assignee":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Assignee","description":"Free-text name; may differ from the linked user's name."},"assignee_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Assignee User Id","description":"Set only when the assignee name matched a Cogram user."},"source_meeting_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Meeting Id"},"source_meeting_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Meeting Name"},"meeting_series_uid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Meeting Series Uid"},"archived":{"type":"boolean","title":"Archived"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At","description":"Null if never updated."},"last_status_changed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Status Changed At","description":"Timestamp of the newest history entry."}},"type":"object","required":["id","project_id","project_name","title","body","status","priority","due_date","assignee","assignee_user_id","source_meeting_id","source_meeting_name","meeting_series_uid","archived","created_at","updated_at","last_status_changed_at"],"title":"BoardItemV1","description":"A single kanban board item.\n\nDropped relative to the internal ``GlobalBoardItemOutSchema``:\n    * ``organization_id`` — implicit from the API key.\nRenamed:\n    * ``modified_at`` → ``updated_at`` (consistent with ``ActionItemV1``)."},"CompanyRefV1":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"}},"type":"object","required":["id","name"],"title":"CompanyRefV1","description":"A company named in a proposed link."},"ConnectorBackupEvent":{"type":"string","enum":["started","finished"],"title":"ConnectorBackupEvent","description":"Lifecycle point a connector reports."},"ConnectorBackupStatus":{"type":"string","enum":["success","failed"],"title":"ConnectorBackupStatus","description":"Outcome of a finished run. Absent on ``started`` events."},"ContactCompanyLinkApplyResultV1":{"properties":{"applied":{"items":{"$ref":"#/components/schemas/AppliedLinkV1"},"type":"array","title":"Applied","description":"Every link this call wrote, contact by contact. Keep it: it names the contacts that held no company before the call, and it is therefore the only way to reverse a run that linked the wrong company — Cogram cannot tell a firm's domain from a consumer one."},"linked":{"type":"integer","title":"Linked","description":"Contacts given a company by this call."},"skipped":{"type":"integer","title":"Skipped","description":"Proposed links this call did not write, because the contact gained a company between the read and the write. An existing company is never overwritten."}},"type":"object","required":["applied","linked","skipped"],"title":"ContactCompanyLinkApplyResultV1","description":"What the write did."},"ContactCompanyLinkApplyV1":{"properties":{"contact_ids":{"anyOf":[{"items":{"type":"string","maxLength":64},"type":"array","maxItems":1000},{"type":"null"}],"title":"Contact Ids","description":"Apply only the proposed links for these contacts. Omit to apply every one. Ids that are not currently proposed are ignored, so a list built from an earlier `GET` cannot link a contact whose company has since been set. The 1000-id ceiling bounds the request body, not the write: omitting the field still applies the whole proposal."}},"type":"object","title":"ContactCompanyLinkApplyV1","description":"Which of the proposed links to write."},"ContactCompanyLinkListV1":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ContactCompanyLinkV1"},"type":"array","title":"Data"},"total":{"type":"integer","title":"Total","description":"Total matches across all pages, not just this page."},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"}},"type":"object","required":["data","total","page","page_size"],"title":"ContactCompanyLinkListV1","description":"Paginated list of proposed contact-to-company links."},"ContactCompanyLinkV1":{"properties":{"contact_id":{"type":"string","title":"Contact Id"},"primary_email":{"type":"string","title":"Primary Email"},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name"},"domain":{"type":"string","title":"Domain","description":"Domain of `primary_email`, lowercased."},"company":{"$ref":"#/components/schemas/CompanyRefV1","description":"The company that would be linked: the oldest non-archived company listing `domain` in its email domains."},"also_claiming":{"items":{"$ref":"#/components/schemas/CompanyRefV1"},"type":"array","title":"Also Claiming","description":"Other non-archived companies listing `domain`, in the order they lost to `company`. Normally empty; when it is not, review before applying — the domain is on more than one company."}},"type":"object","required":["contact_id","primary_email","first_name","last_name","domain","company","also_claiming"],"title":"ContactCompanyLinkV1","description":"One contact with no company, and the company its email domain points at."},"DataExportCreateRequest":{"properties":{"project_ids":{"anyOf":[{"items":{"type":"string"},"type":"array","minItems":1},{"type":"null"}],"title":"Project Ids","description":"Project KSUIDs to scope the data_export to. Omit for an org-wide data_export, which carries the 6-month cap. Duplicates are collapsed."},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id","description":"Deprecated single-project form of `project_ids`.","deprecated":true},"time_filter":{"$ref":"#/components/schemas/TimeFilter","description":"`{mode: range, start, end}` or `{mode: all_time}`. `all_time` is project-scoped only."},"document_formats":{"items":{"type":"string","const":"docx"},"type":"array","title":"Document Formats","description":"Rendered document formats to emit per field report and observation, alongside the JSON. Empty (the default) ships metadata and raw blobs only. Does not affect `meeting.docx`, which every export includes. Rendering makes a build slower and its zip larger."}},"type":"object","required":["time_filter"],"title":"DataExportCreateRequest","description":"Body for ``POST /data_exports`` (both UI and external API).\n\nCap rule is **not** enforced by Pydantic — call ``validate_cap_rule()``\nexplicitly from the service so the structured ``CapRuleViolation`` can\npropagate cleanly to the router (Pydantic would otherwise wrap it in a\n``ValidationError`` and surface a 422 with the wrong shape)."},"DataExportListV1":{"properties":{"data":{"items":{"$ref":"#/components/schemas/DataExportResponse"},"type":"array","title":"Data","description":"Newest first."},"total":{"type":"integer","title":"Total","description":"Total matches across all pages, not just this page."},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"}},"type":"object","required":["data","total","page","page_size"],"title":"DataExportListV1","description":"Paginated data export list response — matches the standard\n``{data, total, page, page_size}`` envelope used across the\nexternal API."},"DataExportPartResponse":{"properties":{"id":{"type":"string","title":"Id"},"part_index":{"type":"integer","title":"Part Index","description":"Zero-indexed."},"size_bytes":{"type":"integer","title":"Size Bytes","description":"Compressed size."},"signed_url":{"type":"string","title":"Signed Url","description":"Pre-signed, valid 1 hour. Re-fetch on each read, never cache."}},"type":"object","required":["id","part_index","size_bytes","signed_url"],"title":"DataExportPartResponse","description":"One zip part within a completed data_export, with a freshly-signed download URL.\n\n``signed_url`` is regenerated by the service on every status read (signing\nis local math, no S3 round-trip) — clients should never cache it past the\n1-hour validity window stamped on the URL itself."},"DataExportProjectRef":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"customer_project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Customer Project Id","description":"Customer's own project label. Not unique — use `id` for identity."}},"type":"object","required":["id","name","customer_project_id"],"title":"DataExportProjectRef","description":"One project in a data_export's scope, with the labels a human needs to\nrecognize it. Only projects that still exist are represented — see\n``DataExportResponse.project_ids`` for the scope as requested."},"DataExportResponse":{"properties":{"id":{"type":"string","title":"Id"},"organization_id":{"type":"string","title":"Organization Id"},"source":{"type":"string","enum":["ui","api"],"title":"Source"},"requester_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Requester User Id","description":"`ui` source only."},"requester_api_key_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Requester Api Key Id","description":"`api` source only."},"requester_api_key_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Requester Api Key Name","description":"`api` source only. Prefer this over parsing `requester_display`."},"requester_forwarded_user":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Requester Forwarded User","description":"`X-Forwarded-User` header value at create time."},"requester_display":{"type":"string","title":"Requester Display","description":"Human-readable attribution: the user's name (their email if the name is empty), `API key '<name>'`, or `<forwarded_user> (via API key '<name>')`. Degrades to `<forwarded_user> (via deleted API key)` or `unknown` when the linked user or key row was deleted after the export was created."},"project_ids":{"items":{"type":"string"},"type":"array","title":"Project Ids","description":"Scope as requested — empty for org-wide exports. Retains ids of projects deleted after the export was created, so `projects` may be shorter."},"projects":{"items":{"$ref":"#/components/schemas/DataExportProjectRef"},"type":"array","title":"Projects","description":"Scope projects that still exist, in `project_ids` order."},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id","description":"Set only for single-project exports; null otherwise.","deprecated":true},"project_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Name","description":"Set only when `project_id` is set.","deprecated":true},"project_customer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Customer Id","description":"Customer's own project label. Not unique — use `project_id` for identity.","deprecated":true},"time_filter":{"$ref":"#/components/schemas/TimeFilter","description":"Echo of the request."},"document_formats":{"items":{"type":"string"},"type":"array","title":"Document Formats","description":"Echo of the request. Typed loosely so a row written by a newer deployment, carrying a format this one does not know, still serializes instead of failing the whole listing."},"status":{"type":"string","enum":["pending","running","cancelling","cancelled","completed","failed","expired"],"title":"Status"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Set when `status` is `failed`."},"requested_at":{"type":"string","format":"date-time","title":"Requested At"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At","description":"Also set on failure and cancellation."},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"Parts are deleted at this time, 7 days after completion."},"parts":{"items":{"$ref":"#/components/schemas/DataExportPartResponse"},"type":"array","title":"Parts","description":"Zip parts with signed URLs. Empty until `status` is `completed`."}},"type":"object","required":["id","organization_id","source","requester_user_id","requester_api_key_id","requester_api_key_name","requester_forwarded_user","requester_display","project_ids","project_id","time_filter","document_formats","status","error","requested_at","started_at","completed_at","expires_at"],"title":"DataExportResponse","description":"Full read shape returned by ``GET /data_exports`` and ``GET /data_exports/{id}``.\n\n``parts`` is empty until the data_export reaches ``COMPLETED``. Even after\n``EXPIRED``, the row remains as an audit record — clients distinguish\n\"downloadable\" from \"audit-only\" via the ``status`` field."},"EmailAuditContextV1":{"properties":{"internet_message_id":{"type":"string","title":"Internet Message Id","description":"The email's RFC 5322 Message-ID. Match it against your mail server or archive."},"sent_at":{"type":"string","format":"date-time","title":"Sent At"},"retention_class":{"anyOf":[{"$ref":"#/components/schemas/RetentionClass"},{"type":"null"}],"description":"The email's retention class, or null when the organization uses no email retention ruleset."},"ruleset":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ruleset","description":"The email retention ruleset the organization used when the email was removed, or null."}},"type":"object","required":["internet_message_id","sent_at","retention_class","ruleset"],"title":"EmailAuditContext","description":"What identifies a removed email outside Cogram. Never any content."},"ErrorCode":{"type":"string","enum":["api_key_missing","api_key_invalid_format","api_key_invalid","api_key_expired","bad_request","forbidden","not_found","method_not_allowed","conflict","validation_error","rate_limit_exceeded","internal_error","range_too_large","data_export_in_progress"],"title":"ErrorCode","description":"Error codes for external API v1."},"ErrorV1":{"properties":{"error":{"$ref":"#/components/schemas/ErrorCode","description":"Stable machine-readable code. Branch on this."},"message":{"type":"string","title":"Message","description":"Human-readable detail. Wording may change."}},"type":"object","required":["error","message"],"title":"ErrorV1","description":"JSON body format for all API v1 error responses.\n\nThis model controls the actual serialization of error responses:\n{\"error\": \"not_found\", \"message\": \"Project 'xyz' not found\"}"},"MeetingAuditContextV1":{"properties":{"meeting_deletion_reason":{"type":"string","title":"Meeting Deletion Reason","description":"The meeting's raw deletion reason, e.g. `CALENDAR_CANCELLATION`."}},"type":"object","required":["meeting_deletion_reason"],"title":"MeetingAuditContext","description":"Why the system deleted a meeting. Set only for reason `OTHER`."},"ProjectCreateV1":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name","description":"Unique within the org."},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"external_id":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"External Id","description":"Your system's ID for this project"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id","description":"Deprecated: use members instead"},"members":{"anyOf":[{"items":{"$ref":"#/components/schemas/ProjectMemberCreateV1"},"type":"array","maxItems":500},{"type":"null"}],"title":"Members","description":"Users to add to the project with their roles"},"client_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Client Name"},"project_address":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Project Address"},"email_filing_enabled":{"type":"boolean","title":"Email Filing Enabled","default":false},"meeting_filing_enabled":{"type":"boolean","title":"Meeting Filing Enabled","description":"Whether Cogram suggests this project for meetings and, where the organization allows it, files meetings into it. Only takes effect while the organization has meeting filing turned on. Filing a meeting here by hand works either way.","default":true},"is_public":{"type":"boolean","title":"Is Public","description":"If true, project appears in org project directory; non-members can request access","default":false},"template_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Template Id","description":"Project template to apply. The project gets the template's project type and its groups with their roles. email_filing_enabled and is_public take the template's defaults unless sent. Resolved as the API key's creator, who needs read access to project templates."}},"type":"object","required":["name"],"title":"ProjectCreateV1","description":"Create project request."},"ProjectIncludeV1":{"type":"string","enum":["counts","members"],"title":"ProjectIncludeV1","description":"Optional response sections, requested via ``?include=``."},"ProjectListV1":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ProjectV1"},"type":"array","title":"Data"},"total":{"type":"integer","title":"Total","description":"Total matches across all pages, not just this page."},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"}},"type":"object","required":["data","total","page","page_size"],"title":"ProjectListV1","description":"Paginated project list response."},"ProjectMarketSectorV1":{"properties":{"name":{"type":"string","title":"Name","description":"Sector name as configured in Unanet, e.g. \"Healthcare\"."},"pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Pct","description":"Percent of the project attributed to this sector."}},"type":"object","required":["name"],"title":"ProjectMarketSectorV1","description":"One Unanet market sector on a project, with the share of the project\nattributed to it. Reported exactly as Unanet holds it — Cogram neither\nnormalizes the percentages nor requires them to add up."},"ProjectMemberCreateV1":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"role":{"$ref":"#/components/schemas/ProjectMemberRoleV1"}},"type":"object","required":["user_id","role"],"title":"ProjectMemberCreateV1","description":"Member entry for project creation."},"ProjectMemberRoleV1":{"type":"string","enum":["MEMBER","OWNER","LEAD","VIEWER"],"title":"ProjectMemberRoleV1","description":"Per-project role: OWNER manages membership, LEAD manages the project but not\nmembership, MEMBER read/write, VIEWER read-only."},"ProjectMemberSummaryV1":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"email":{"type":"string","title":"Email"},"role":{"$ref":"#/components/schemas/ProjectMemberRoleV1"}},"type":"object","required":["user_id","email","role"],"title":"ProjectMemberSummaryV1","description":"One member inside a project response, as returned by\n``?include=members``.\n\nNarrower than ``ProjectMemberV1`` from ``GET /projects/{id}/members``,\nwhich also carries first and last name: a roster embedded in every row of a\n100-project page pays for each field it carries, and display names are not\nwhat the roster is read for. Fetch the dedicated members endpoint for those.\n\n``user_id`` stays, though, because ``email`` cannot stand in for it. Two\naccounts may share an address (see ``User``), so an email-only entry is\nambiguous exactly when it matters, and it is ``user_id`` that the write\nside — ``PUT``/``DELETE /projects/{id}/members/{user_id}`` — is keyed by."},"ProjectMemberUpdateV1":{"properties":{"role":{"$ref":"#/components/schemas/ProjectMemberRoleV1"}},"type":"object","required":["role"],"title":"ProjectMemberUpdateV1","description":"Add/update project member request."},"ProjectMemberV1":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"email":{"type":"string","title":"Email"},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name"},"role":{"$ref":"#/components/schemas/ProjectMemberRoleV1"}},"type":"object","required":["user_id","email","first_name","last_name","role"],"title":"ProjectMemberV1","description":"Project member response."},"ProjectRelatedCountsV1":{"properties":{"emails":{"type":"integer","title":"Emails","default":0},"documents":{"type":"integer","title":"Documents","default":0},"drawings":{"type":"integer","title":"Drawings","default":0},"drawing_sets":{"type":"integer","title":"Drawing Sets","default":0},"submittals":{"type":"integer","title":"Submittals","default":0},"rfis":{"type":"integer","title":"Rfis","default":0},"meetings":{"type":"integer","title":"Meetings","default":0},"reports":{"type":"integer","title":"Reports","default":0},"observations":{"type":"integer","title":"Observations","default":0},"transmittals":{"type":"integer","title":"Transmittals","default":0}},"type":"object","title":"ProjectRelatedCountsV1","description":"How many records of each type the project holds.\n\nEvery field defaults to 0, so a project with nothing filed under it\nreturns zeros rather than omitting keys.\n\nCounts cover the record types an all-time export of the same project\nships, with one exception: soft-deleted meetings are excluded, because\nthey are absent from every other read path. An export narrowed to a time\nrange ships a subset of what is counted here. Cogram's seeded example\nrecords are counted; they are real rows in the project.\n\nUsable as a change signal: poll, compare against the previous values, and\nre-export the projects that moved. Three classes of change are invisible\nto it, because cardinality does not move — an in-place edit (a renamed\ndocument, a corrected email subject); a new revision of an existing\ndrawing, or action items generated for an existing meeting, since only\nthe top-level record is counted; and an add plus a delete inside the same\npolling window, which nets to zero."},"ProjectTemplateGroupInV1":{"properties":{"group_id":{"type":"string","maxLength":64,"title":"Group Id"},"role":{"type":"string","maxLength":64,"title":"Role","description":"MEMBER, VIEWER, or LEAD."}},"type":"object","required":["group_id","role"],"title":"ProjectTemplateGroupInV1","description":"A group to add to every project created from the template."},"ProjectTemplateGroupV1":{"properties":{"group_id":{"type":"string","title":"Group Id"},"group_name":{"type":"string","title":"Group Name"},"role":{"type":"string","enum":["LEAD","MEMBER","VIEWER"],"title":"ProjectGroupRole","description":"Roles a group can hold on a project. OWNER stays direct-only by design;\nLEAD may be granted via a group (gated behind the project-lead-role flag at\nthe assignment routes, like a direct LEAD)."},"member_count":{"type":"integer","title":"Member Count"}},"type":"object","required":["group_id","group_name","role","member_count"],"title":"ProjectTemplateGroupV1","description":"A group bound to a template."},"ProjectTemplateListV1":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ProjectTemplateSummaryV1"},"type":"array","title":"Data"},"total":{"type":"integer","title":"Total","description":"Total matches across all pages, not just this page."},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"}},"type":"object","required":["data","total","page","page_size"],"title":"ProjectTemplateListV1","description":"Paginated project-template list response."},"ProjectTemplateSummaryV1":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"group_count":{"type":"integer","title":"Group Count"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At","description":"Null if never updated."}},"type":"object","required":["id","name","description","group_count","created_at","updated_at"],"title":"ProjectTemplateSummaryV1","description":"Project template, as listed."},"ProjectTemplateV1":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"groups":{"items":{"$ref":"#/components/schemas/ProjectTemplateGroupV1"},"type":"array","title":"Groups"},"project_type_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Type Id"},"email_filing_enabled_default":{"type":"boolean","title":"Email Filing Enabled Default"},"is_public_default":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Public Default"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At","description":"Null if never updated."}},"type":"object","required":["id","name","description","groups","project_type_id","email_filing_enabled_default","is_public_default","created_at","updated_at"],"title":"ProjectTemplateV1","description":"Project template with its group bindings."},"ProjectTemplateWriteV1":{"properties":{"name":{"type":"string","maxLength":100,"minLength":1,"title":"Name","description":"Unique within the org (case-insensitive)."},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"groups":{"items":{"$ref":"#/components/schemas/ProjectTemplateGroupInV1"},"type":"array","maxItems":500,"title":"Groups"},"project_type_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Project Type Id"},"email_filing_enabled_default":{"type":"boolean","title":"Email Filing Enabled Default","default":false},"is_public_default":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Public Default"}},"type":"object","required":["name"],"title":"ProjectTemplateWriteV1","description":"Create or replace a project template. Every field is written."},"ProjectTypeCreateV1":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name","description":"Unique within the org (case-insensitive)."},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color","description":"Optional hex color for UI display, e.g. '#3B82F6'."}},"type":"object","required":["name"],"title":"ProjectTypeCreateV1","description":"Create project-type request."},"ProjectTypeListV1":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ProjectTypeV1"},"type":"array","title":"Data"},"total":{"type":"integer","title":"Total","description":"Total matches across all pages, not just this page."},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"}},"type":"object","required":["data","total","page","page_size"],"title":"ProjectTypeListV1","description":"Paginated project-type list response."},"ProjectTypeUpdateV1":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name","description":"Unique within the org (case-insensitive)."},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color","description":"Optional hex color for UI display, e.g. '#3B82F6'."}},"type":"object","title":"ProjectTypeUpdateV1","description":"Update project-type request.\n\nAll fields are optional - only provided fields will be updated."},"ProjectTypeV1":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"},"projects_count":{"type":"integer","title":"Projects Count"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At","description":"Null if never updated."}},"type":"object","required":["id","name","color","projects_count","created_at","updated_at"],"title":"ProjectTypeV1","description":"Project-type response."},"ProjectUpdateV1":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name","description":"Unique within the org."},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"external_id":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"External Id","description":"Your system's ID for this project"},"client_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Client Name"},"project_address":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Project Address"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","description":"Set to false to archive the project"},"email_filing_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Email Filing Enabled"},"meeting_filing_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Meeting Filing Enabled","description":"Whether Cogram suggests this project for meetings and, where the organization allows it, files meetings into it. Only takes effect while the organization has meeting filing turned on. Filing a meeting here by hand works either way."},"is_public":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Public","description":"If true, project appears in org project directory; non-members can request access"}},"type":"object","title":"ProjectUpdateV1","description":"Update project request.\n\nAll fields are optional - only provided fields will be updated."},"ProjectV1":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id","description":"Your system's ID for this project."},"client_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Name"},"project_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Address"},"is_active":{"type":"boolean","title":"Is Active","description":"False once the project is archived."},"email_filing_enabled":{"type":"boolean","title":"Email Filing Enabled"},"meeting_filing_enabled":{"type":"boolean","title":"Meeting Filing Enabled","description":"Whether Cogram suggests this project for meetings and, where the organization allows it, files meetings into it. Only takes effect while the organization has meeting filing turned on. Filing a meeting here by hand works either way."},"is_public":{"type":"boolean","title":"Is Public","description":"If true, project appears in org project directory; non-members can request access."},"unanet_project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unanet Project Id","description":"Unanet ERP A/E project id, for projects synced from Unanet; null for every other project. Stable across customer re-codes, so join on this rather than on external_id."},"unanet_lead_stage":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unanet Lead Stage","description":"Unanet lead/pursuit stage name (e.g. \"06-Submitted Proposal\"). Set means an open opportunity; null means a normal project."},"unanet_custom_fields":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Unanet Custom Fields","description":"The project's Unanet user-defined fields, keyed by Unanet fieldName. Replaced in full on each sync, so a cleared field disappears."},"unanet_market_sectors":{"anyOf":[{"items":{"$ref":"#/components/schemas/ProjectMarketSectorV1"},"type":"array"},{"type":"null"}],"title":"Unanet Market Sectors","description":"The project's Unanet market sectors and their percentage split, in Unanet's order. Null for projects that aren't synced from Unanet or have no sectors."},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At","description":"Tracks the project record only — not the emails, RFIs, meetings or other records filed under it. Null if never updated."},"related_counts":{"anyOf":[{"$ref":"#/components/schemas/ProjectRelatedCountsV1"},{"type":"null"}],"description":"Per-type counts of records filed under this project. Present only when the request asked for it with ?include=counts; null otherwise, so existing callers pay nothing for it."},"members":{"anyOf":[{"items":{"$ref":"#/components/schemas/ProjectMemberSummaryV1"},"type":"array"},{"type":"null"}],"title":"Members","description":"Users holding a role on this project, ordered by email. Present only when the request asked for it with ?include=members; null otherwise, so existing callers pay nothing for it. An empty list means the project genuinely has no members — distinguishable from null, which means they were not requested. Group-granted access is not listed; only direct per-user roles are."}},"type":"object","required":["id","name","description","external_id","client_name","project_address","is_active","email_filing_enabled","meeting_filing_enabled","is_public","unanet_project_id","unanet_lead_stage","unanet_custom_fields","unanet_market_sectors","created_at","updated_at"],"title":"ProjectV1","description":"Project response."},"RetentionClass":{"type":"string","enum":["SHORT","ARCHIVE"],"title":"RetentionClass","description":"The retention class an email retention ruleset gives an email. The\nruleset maps each class to a duration; an email without a class\n(NULL) never expires."},"TimeFilter":{"oneOf":[{"$ref":"#/components/schemas/TimeRangeFilter"},{"$ref":"#/components/schemas/AllTimeFilter"}],"title":"TimeFilter","description":"Discriminated-union wrapper so the JSONB column can use ``PydanticJsonbType``.\n\nStored shape mirrors ``root`` directly (``RootModel`` serializes without\na wrapping field), e.g. ``{\"mode\": \"range\", \"start\": ..., \"end\": ...}``.","discriminator":{"propertyName":"mode","mapping":{"all_time":"#/components/schemas/AllTimeFilter","range":"#/components/schemas/TimeRangeFilter"}}},"TimeRangeFilter":{"properties":{"mode":{"type":"string","const":"range","title":"Mode","default":"range"},"start":{"type":"string","format":"date-time","title":"Start","description":"Inclusive. Must be timezone-aware."},"end":{"type":"string","format":"date-time","title":"End","description":"Inclusive. Must be timezone-aware."}},"type":"object","required":["start","end"],"title":"TimeRangeFilter","description":"Closed time range. Both bounds timezone-aware; ``end`` must be >= ``start``."},"UserListV1":{"properties":{"data":{"items":{"$ref":"#/components/schemas/UserV1"},"type":"array","title":"Data"},"total":{"type":"integer","title":"Total","description":"Total matches across all pages, not just this page."},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"}},"type":"object","required":["data","total","page","page_size"],"title":"UserListV1","description":"Paginated user list response."},"UserRoleV1":{"type":"string","enum":["MEMBER","ADMIN","GROUP_ADMIN","OWNER","INTERNAL_ADMIN"],"title":"UserRoleV1","description":"All user roles (used in responses)."},"UserUpdateV1":{"properties":{"roles":{"items":{"$ref":"#/components/schemas/AssignableUserRoleV1"},"type":"array","title":"Roles","description":"Replaces the current role set. Must be non-empty."}},"type":"object","required":["roles"],"title":"UserUpdateV1","description":"Update user roles request."},"UserV1":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"email":{"type":"string","title":"Email"},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name"},"roles":{"items":{"$ref":"#/components/schemas/UserRoleV1"},"type":"array","title":"Roles","description":"Organization-level roles, not project roles."},"is_sso_user":{"type":"boolean","title":"Is Sso User"}},"type":"object","required":["id","email","first_name","last_name","roles","is_sso_user"],"title":"UserV1","description":"User response."}},"securitySchemes":{"HTTPBearer":{"type":"http","description":"Organization API key. Minted by admins in Organization Settings → Integrations → API Keys; shown once, may carry an expiry.","scheme":"bearer","bearerFormat":"Cogram organization API key"}}},"tags":[{"name":"projects","description":"Projects and their membership/roles."},{"name":"project-types","description":"Project-type taxonomy used to classify projects (e.g. \"Design-Build\")."},{"name":"project-templates","description":"Reusable project setups: a project type plus groups with roles, applied when a project is created with a template_id."},{"name":"users","description":"Organization members and their organization roles."},{"name":"action-items","description":"Read-only action items from meetings."},{"name":"board-items","description":"Read-only kanban board items, with change history."},{"name":"data-exports","description":"Zip exports of org data: request, poll, download."},{"name":"backup-runs","description":"Report backup runs so Cogram can alert when they stop."},{"name":"audit-log","description":"Read-only log of the emails and meetings Cogram deleted or redacted, and why."},{"name":"directory","description":"Directory reconciliation: propose and apply the company link that contacts created outside a transmittal send are missing."}],"servers":[{"url":"/v1"}]}