{
  "openapi": "3.1.0",
  "info": {
    "title": "Slashy Enterprise API",
    "version": "1.0",
    "description": "Read-only, server-to-server access to your team's Slashy mail metadata, calendars and analytics. Authenticate with `Authorization: Bearer slsh_live_...`; requests that carry a browser `Origin` header are refused. A key reads at most what the admin who created it can read, never drafts. Errors are `{\"detail\": {\"error\": code, \"message\": text}}`, except 422 (invalid parameters). See README.md for scopes, limits and a warehouse-sync recipe."
  },
  "servers": [
    {
      "url": "https://api.slashy.com"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Account",
      "description": "The calling key."
    },
    {
      "name": "Directory",
      "description": "Members and inboxes the key can read."
    },
    {
      "name": "Mail",
      "description": "Threads, messages and search. Metadata by default; content with `mail.content.read`."
    },
    {
      "name": "Calendar",
      "description": "Members' primary calendars."
    },
    {
      "name": "Analytics",
      "description": "Team response-time analytics."
    }
  ],
  "paths": {
    "/v1/me": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Describe the calling key",
        "operationId": "getMe",
        "description": "The key's id, name, scopes and expiry, its team, and how many inboxes it can read. Works with any valid key, whatever its scopes: a cheap way to check a key.\n\n**Scope:** none (any valid key). **Rate limit class:** `read` (600/minute per key).",
        "x-required-scope": null,
        "x-rate-limit-class": "read",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                },
                "example": {
                  "key": {
                    "id": "k7q2m4x6w3z5r2tp",
                    "name": "Warehouse sync",
                    "scopes": [
                      "analytics.read",
                      "calendar.read",
                      "directory.read",
                      "mail.metadata.read"
                    ],
                    "expires_at": "2027-10-14T18:00:00+00:00"
                  },
                  "team_id": "0b7d3c52-9e1a-4f6b-8c2d-5a4e3f2b1c09",
                  "readable_inbox_count": 42
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown, revoked or expired API key. `error` is one of: `invalid_api_key`, `api_key_revoked`, `api_key_expired`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_api_key": {
                    "$ref": "#/components/examples/invalid_api_key"
                  },
                  "api_key_revoked": {
                    "$ref": "#/components/examples/api_key_revoked"
                  },
                  "api_key_expired": {
                    "$ref": "#/components/examples/api_key_expired"
                  }
                }
              }
            },
            "x-error-codes": [
              "invalid_api_key",
              "api_key_revoked",
              "api_key_expired"
            ],
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWWAuthenticate"
              }
            }
          },
          "403": {
            "description": "The key is valid but may not make this call. `error` is one of: `browser_not_allowed`, `ip_not_allowed`, `not_enterprise`, `key_owner_inactive`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "browser_not_allowed": {
                    "$ref": "#/components/examples/browser_not_allowed"
                  },
                  "ip_not_allowed": {
                    "$ref": "#/components/examples/ip_not_allowed"
                  },
                  "not_enterprise": {
                    "$ref": "#/components/examples/not_enterprise"
                  },
                  "key_owner_inactive": {
                    "$ref": "#/components/examples/key_owner_inactive"
                  }
                }
              }
            },
            "x-error-codes": [
              "browser_not_allowed",
              "ip_not_allowed",
              "not_enterprise",
              "key_owner_inactive"
            ]
          },
          "429": {
            "description": "Too many requests. Wait `Retry-After` seconds, then retry. `error` is one of: `too_many_auth_failures`, `rate_limited`, `too_many_concurrent_requests`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "too_many_auth_failures": {
                    "$ref": "#/components/examples/too_many_auth_failures"
                  },
                  "rate_limited": {
                    "summary": "429 rate_limited",
                    "value": {
                      "detail": {
                        "error": "rate_limited",
                        "message": "Rate limit exceeded (600/minute)."
                      }
                    }
                  },
                  "too_many_concurrent_requests": {
                    "$ref": "#/components/examples/too_many_concurrent_requests"
                  }
                }
              }
            },
            "x-error-codes": [
              "too_many_auth_failures",
              "rate_limited",
              "too_many_concurrent_requests"
            ],
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/v1/members": {
      "get": {
        "tags": [
          "Directory"
        ],
        "summary": "List members whose mail the key can read",
        "operationId": "listMembers",
        "description": "Each member the key can read, with their inboxes, sorted by email. That is the key's creator plus the members the creator can read in Slashy, narrowed to the members the key is limited to.\n\n**Scope:** `directory.read`. **Rate limit class:** `read` (600/minute per key).",
        "x-required-scope": "directory.read",
        "x-rate-limit-class": "read",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MemberList"
                },
                "example": {
                  "members": [
                    {
                      "user_id": "7a9c1e3f-5b2d-4c6e-8a0f-1b3d5f7a9c21",
                      "email": "jordan@example.com",
                      "inboxes": [
                        {
                          "id": "5b0c1f8e-2a43-4d7b-9f61-3c2e8a9d4b10",
                          "email": "jordan@example.com",
                          "owner_email": "jordan@example.com"
                        }
                      ]
                    },
                    {
                      "user_id": "e1f3a5c7-9b2d-4e6f-8a1c-3b5d7f9a1c42",
                      "email": "sam@example.com",
                      "inboxes": [
                        {
                          "id": "c2e4a6b8-1d3f-4a5c-9e7b-2f4d6a8c0e13",
                          "email": "sam@example.com",
                          "owner_email": "sam@example.com"
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown, revoked or expired API key. `error` is one of: `invalid_api_key`, `api_key_revoked`, `api_key_expired`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_api_key": {
                    "$ref": "#/components/examples/invalid_api_key"
                  },
                  "api_key_revoked": {
                    "$ref": "#/components/examples/api_key_revoked"
                  },
                  "api_key_expired": {
                    "$ref": "#/components/examples/api_key_expired"
                  }
                }
              }
            },
            "x-error-codes": [
              "invalid_api_key",
              "api_key_revoked",
              "api_key_expired"
            ],
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWWAuthenticate"
              }
            }
          },
          "403": {
            "description": "The key is valid but may not make this call. `error` is one of: `browser_not_allowed`, `ip_not_allowed`, `not_enterprise`, `key_owner_inactive`, `insufficient_scope`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "browser_not_allowed": {
                    "$ref": "#/components/examples/browser_not_allowed"
                  },
                  "ip_not_allowed": {
                    "$ref": "#/components/examples/ip_not_allowed"
                  },
                  "not_enterprise": {
                    "$ref": "#/components/examples/not_enterprise"
                  },
                  "key_owner_inactive": {
                    "$ref": "#/components/examples/key_owner_inactive"
                  },
                  "insufficient_scope": {
                    "summary": "403 insufficient_scope",
                    "value": {
                      "detail": {
                        "error": "insufficient_scope",
                        "message": "This API key does not have the directory.read scope."
                      }
                    }
                  }
                }
              }
            },
            "x-error-codes": [
              "browser_not_allowed",
              "ip_not_allowed",
              "not_enterprise",
              "key_owner_inactive",
              "insufficient_scope"
            ]
          },
          "429": {
            "description": "Too many requests. Wait `Retry-After` seconds, then retry. `error` is one of: `too_many_auth_failures`, `rate_limited`, `too_many_concurrent_requests`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "too_many_auth_failures": {
                    "$ref": "#/components/examples/too_many_auth_failures"
                  },
                  "rate_limited": {
                    "summary": "429 rate_limited",
                    "value": {
                      "detail": {
                        "error": "rate_limited",
                        "message": "Rate limit exceeded (600/minute)."
                      }
                    }
                  },
                  "too_many_concurrent_requests": {
                    "$ref": "#/components/examples/too_many_concurrent_requests"
                  }
                }
              }
            },
            "x-error-codes": [
              "too_many_auth_failures",
              "rate_limited",
              "too_many_concurrent_requests"
            ],
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/v1/inboxes": {
      "get": {
        "tags": [
          "Directory"
        ],
        "summary": "List inboxes the key can read",
        "operationId": "listInboxes",
        "description": "Every inbox the key can read. Use these ids as `inbox_id` elsewhere.\n\n**Scope:** `directory.read`. **Rate limit class:** `read` (600/minute per key).",
        "x-required-scope": "directory.read",
        "x-rate-limit-class": "read",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxList"
                },
                "example": {
                  "inboxes": [
                    {
                      "id": "5b0c1f8e-2a43-4d7b-9f61-3c2e8a9d4b10",
                      "email": "jordan@example.com",
                      "owner_email": "jordan@example.com"
                    },
                    {
                      "id": "c2e4a6b8-1d3f-4a5c-9e7b-2f4d6a8c0e13",
                      "email": "sam@example.com",
                      "owner_email": "sam@example.com"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown, revoked or expired API key. `error` is one of: `invalid_api_key`, `api_key_revoked`, `api_key_expired`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_api_key": {
                    "$ref": "#/components/examples/invalid_api_key"
                  },
                  "api_key_revoked": {
                    "$ref": "#/components/examples/api_key_revoked"
                  },
                  "api_key_expired": {
                    "$ref": "#/components/examples/api_key_expired"
                  }
                }
              }
            },
            "x-error-codes": [
              "invalid_api_key",
              "api_key_revoked",
              "api_key_expired"
            ],
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWWAuthenticate"
              }
            }
          },
          "403": {
            "description": "The key is valid but may not make this call. `error` is one of: `browser_not_allowed`, `ip_not_allowed`, `not_enterprise`, `key_owner_inactive`, `insufficient_scope`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "browser_not_allowed": {
                    "$ref": "#/components/examples/browser_not_allowed"
                  },
                  "ip_not_allowed": {
                    "$ref": "#/components/examples/ip_not_allowed"
                  },
                  "not_enterprise": {
                    "$ref": "#/components/examples/not_enterprise"
                  },
                  "key_owner_inactive": {
                    "$ref": "#/components/examples/key_owner_inactive"
                  },
                  "insufficient_scope": {
                    "summary": "403 insufficient_scope",
                    "value": {
                      "detail": {
                        "error": "insufficient_scope",
                        "message": "This API key does not have the directory.read scope."
                      }
                    }
                  }
                }
              }
            },
            "x-error-codes": [
              "browser_not_allowed",
              "ip_not_allowed",
              "not_enterprise",
              "key_owner_inactive",
              "insufficient_scope"
            ]
          },
          "429": {
            "description": "Too many requests. Wait `Retry-After` seconds, then retry. `error` is one of: `too_many_auth_failures`, `rate_limited`, `too_many_concurrent_requests`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "too_many_auth_failures": {
                    "$ref": "#/components/examples/too_many_auth_failures"
                  },
                  "rate_limited": {
                    "summary": "429 rate_limited",
                    "value": {
                      "detail": {
                        "error": "rate_limited",
                        "message": "Rate limit exceeded (600/minute)."
                      }
                    }
                  },
                  "too_many_concurrent_requests": {
                    "$ref": "#/components/examples/too_many_concurrent_requests"
                  }
                }
              }
            },
            "x-error-codes": [
              "too_many_auth_failures",
              "rate_limited",
              "too_many_concurrent_requests"
            ],
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/v1/threads": {
      "get": {
        "tags": [
          "Mail"
        ],
        "summary": "List threads in an inbox",
        "operationId": "listThreads",
        "description": "Every thread in one inbox, newest activity first, with its messages. Keyset pagination: pass the previous page's `next_cursor` as `cursor` until `next_cursor` is `null`. A page can hold fewer than `limit` threads, even none, and still have a `next_cursor`. There is no date filter: to sync, walk the whole inbox (see the guide). A thread that gets new activity mid-walk moves to the front, so the next walk picks it up.\n\nUnsent drafts and scheduled sends are never returned: threads holding only those are skipped. Subjects, snippets, attachment file names and to-do summaries need `mail.content.read`.\n\n**Scope:** `mail.metadata.read`. **Rate limit class:** `read` (600/minute per key).",
        "x-required-scope": "mail.metadata.read",
        "x-rate-limit-class": "read",
        "parameters": [
          {
            "name": "inbox_id",
            "in": "query",
            "required": true,
            "description": "An inbox id from `/v1/inboxes`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Threads per page, 1-500.",
            "schema": {
              "type": "integer",
              "maximum": 500,
              "minimum": 1,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "`next_cursor` from the previous page. Omit for the first page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ThreadPage"
                },
                "examples": {
                  "metadata": {
                    "summary": "Key without mail.content.read",
                    "value": {
                      "threads": [
                        {
                          "id": "18f3a2b4c5d6e7f8",
                          "inbox_id": "5b0c1f8e-2a43-4d7b-9f61-3c2e8a9d4b10",
                          "inbox_email": "jordan@example.com",
                          "owner_email": "jordan@example.com",
                          "category": "Important",
                          "labels": [
                            "INBOX",
                            "IMPORTANT",
                            "SENT"
                          ],
                          "first_message_at": "2026-10-13T16:02:11+00:00",
                          "last_message_at": "2026-10-14T09:15:40+00:00",
                          "todo": {
                            "type": "followup",
                            "urgency": "medium",
                            "due_at": "2026-10-17T16:00:00+00:00",
                            "updated_at": "2026-10-14T09:16:02+00:00"
                          },
                          "message_count": 2,
                          "messages": [
                            {
                              "id": "18f3a2b4c5d6e7f8",
                              "message_id": "<18f3a2b4c5d6e7f8@mail.example.com>",
                              "in_reply_to": null,
                              "date": "2026-10-13T16:02:11+00:00",
                              "received_at": "2026-10-13T16:02:13+00:00",
                              "from": {
                                "email": "pat@customer.example.com",
                                "name": "Pat Lee"
                              },
                              "to": [
                                {
                                  "email": "jordan@example.com",
                                  "name": "Jordan Kim"
                                }
                              ],
                              "cc": [],
                              "bcc": [],
                              "labels": [
                                "INBOX",
                                "IMPORTANT"
                              ],
                              "is_read": true,
                              "is_starred": false,
                              "is_sent": false,
                              "is_trash": false,
                              "is_spam": false,
                              "size_bytes": 48211,
                              "attachments": [
                                {
                                  "mime_type": "application/pdf",
                                  "size_bytes": 40960
                                }
                              ]
                            },
                            {
                              "id": "18f3a9d0e1f2a3b4",
                              "message_id": "<18f3a9d0e1f2a3b4@mail.example.com>",
                              "in_reply_to": "<18f3a2b4c5d6e7f8@mail.example.com>",
                              "date": "2026-10-14T09:15:40+00:00",
                              "received_at": "2026-10-14T09:15:40+00:00",
                              "from": {
                                "email": "jordan@example.com",
                                "name": "Jordan Kim"
                              },
                              "to": [
                                {
                                  "email": "pat@customer.example.com",
                                  "name": "Pat Lee"
                                }
                              ],
                              "cc": [],
                              "bcc": [],
                              "labels": [
                                "SENT"
                              ],
                              "is_read": true,
                              "is_starred": false,
                              "is_sent": true,
                              "is_trash": false,
                              "is_spam": false,
                              "size_bytes": 6120,
                              "attachments": []
                            }
                          ]
                        }
                      ],
                      "next_cursor": "MjAyNi0xMC0xM1QxNjowMjoxMSswMDowMHwxOGYzYTJiNGM1ZDZlN2Y4"
                    }
                  },
                  "content": {
                    "summary": "Key with mail.content.read",
                    "value": {
                      "threads": [
                        {
                          "id": "18f3a2b4c5d6e7f8",
                          "inbox_id": "5b0c1f8e-2a43-4d7b-9f61-3c2e8a9d4b10",
                          "inbox_email": "jordan@example.com",
                          "owner_email": "jordan@example.com",
                          "category": "Important",
                          "labels": [
                            "INBOX",
                            "IMPORTANT",
                            "SENT"
                          ],
                          "first_message_at": "2026-10-13T16:02:11+00:00",
                          "last_message_at": "2026-10-14T09:15:40+00:00",
                          "todo": {
                            "type": "followup",
                            "urgency": "medium",
                            "due_at": "2026-10-17T16:00:00+00:00",
                            "updated_at": "2026-10-14T09:16:02+00:00",
                            "summary": "Follow up if Pat hasn't signed the renewal by Friday"
                          },
                          "message_count": 2,
                          "messages": [
                            {
                              "id": "18f3a2b4c5d6e7f8",
                              "message_id": "<18f3a2b4c5d6e7f8@mail.example.com>",
                              "in_reply_to": null,
                              "date": "2026-10-13T16:02:11+00:00",
                              "received_at": "2026-10-13T16:02:13+00:00",
                              "from": {
                                "email": "pat@customer.example.com",
                                "name": "Pat Lee"
                              },
                              "to": [
                                {
                                  "email": "jordan@example.com",
                                  "name": "Jordan Kim"
                                }
                              ],
                              "cc": [],
                              "bcc": [],
                              "labels": [
                                "INBOX",
                                "IMPORTANT"
                              ],
                              "is_read": true,
                              "is_starred": false,
                              "is_sent": false,
                              "is_trash": false,
                              "is_spam": false,
                              "size_bytes": 48211,
                              "attachments": [
                                {
                                  "mime_type": "application/pdf",
                                  "size_bytes": 40960,
                                  "filename": "Quote-2027.pdf"
                                }
                              ],
                              "subject": "Renewal quote for 2027",
                              "snippet": "Hi Jordan, attached is the signed quote. Can you confirm the start date?"
                            },
                            {
                              "id": "18f3a9d0e1f2a3b4",
                              "message_id": "<18f3a9d0e1f2a3b4@mail.example.com>",
                              "in_reply_to": "<18f3a2b4c5d6e7f8@mail.example.com>",
                              "date": "2026-10-14T09:15:40+00:00",
                              "received_at": "2026-10-14T09:15:40+00:00",
                              "from": {
                                "email": "jordan@example.com",
                                "name": "Jordan Kim"
                              },
                              "to": [
                                {
                                  "email": "pat@customer.example.com",
                                  "name": "Pat Lee"
                                }
                              ],
                              "cc": [],
                              "bcc": [],
                              "labels": [
                                "SENT"
                              ],
                              "is_read": true,
                              "is_starred": false,
                              "is_sent": true,
                              "is_trash": false,
                              "is_spam": false,
                              "size_bytes": 6120,
                              "attachments": [],
                              "subject": "Re: Renewal quote for 2027",
                              "snippet": "Thanks Pat, confirming November 1. I'll send the invoice this week."
                            }
                          ],
                          "subject": "Renewal quote for 2027"
                        }
                      ],
                      "next_cursor": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request. `error` is one of: `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_cursor": {
                    "$ref": "#/components/examples/invalid_cursor"
                  }
                }
              }
            },
            "x-error-codes": [
              "invalid_cursor"
            ]
          },
          "401": {
            "description": "Missing, malformed, unknown, revoked or expired API key. `error` is one of: `invalid_api_key`, `api_key_revoked`, `api_key_expired`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_api_key": {
                    "$ref": "#/components/examples/invalid_api_key"
                  },
                  "api_key_revoked": {
                    "$ref": "#/components/examples/api_key_revoked"
                  },
                  "api_key_expired": {
                    "$ref": "#/components/examples/api_key_expired"
                  }
                }
              }
            },
            "x-error-codes": [
              "invalid_api_key",
              "api_key_revoked",
              "api_key_expired"
            ],
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWWAuthenticate"
              }
            }
          },
          "403": {
            "description": "The key is valid but may not make this call. `error` is one of: `browser_not_allowed`, `ip_not_allowed`, `not_enterprise`, `key_owner_inactive`, `insufficient_scope`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "browser_not_allowed": {
                    "$ref": "#/components/examples/browser_not_allowed"
                  },
                  "ip_not_allowed": {
                    "$ref": "#/components/examples/ip_not_allowed"
                  },
                  "not_enterprise": {
                    "$ref": "#/components/examples/not_enterprise"
                  },
                  "key_owner_inactive": {
                    "$ref": "#/components/examples/key_owner_inactive"
                  },
                  "insufficient_scope": {
                    "summary": "403 insufficient_scope",
                    "value": {
                      "detail": {
                        "error": "insufficient_scope",
                        "message": "This API key does not have the mail.metadata.read scope."
                      }
                    }
                  }
                }
              }
            },
            "x-error-codes": [
              "browser_not_allowed",
              "ip_not_allowed",
              "not_enterprise",
              "key_owner_inactive",
              "insufficient_scope"
            ]
          },
          "404": {
            "description": "Not found, or not readable by this key (the two look the same). `error` is one of: `inbox_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "inbox_not_found": {
                    "$ref": "#/components/examples/inbox_not_found"
                  }
                }
              }
            },
            "x-error-codes": [
              "inbox_not_found"
            ]
          },
          "422": {
            "description": "A parameter is missing or invalid. The body lists each problem (not the `Error` shape).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                },
                "example": {
                  "detail": [
                    {
                      "type": "missing",
                      "loc": [
                        "query",
                        "inbox_id"
                      ],
                      "msg": "Field required",
                      "input": null
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait `Retry-After` seconds, then retry. `error` is one of: `too_many_auth_failures`, `rate_limited`, `too_many_concurrent_requests`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "too_many_auth_failures": {
                    "$ref": "#/components/examples/too_many_auth_failures"
                  },
                  "rate_limited": {
                    "summary": "429 rate_limited",
                    "value": {
                      "detail": {
                        "error": "rate_limited",
                        "message": "Rate limit exceeded (600/minute)."
                      }
                    }
                  },
                  "too_many_concurrent_requests": {
                    "$ref": "#/components/examples/too_many_concurrent_requests"
                  }
                }
              }
            },
            "x-error-codes": [
              "too_many_auth_failures",
              "rate_limited",
              "too_many_concurrent_requests"
            ],
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable. Retry the same request later. `error` is one of: `page_unavailable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "page_unavailable": {
                    "$ref": "#/components/examples/page_unavailable"
                  }
                }
              }
            },
            "x-error-codes": [
              "page_unavailable"
            ],
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfterOptional"
              }
            }
          }
        }
      }
    },
    "/v1/threads/{inbox_id}/{thread_id}": {
      "get": {
        "tags": [
          "Mail"
        ],
        "summary": "Get one thread",
        "operationId": "getThread",
        "description": "One thread with its messages, oldest first. `thread_not_found` if there is no such thread in the inbox, or it holds only drafts. URL-encode the thread id.\n\n**Scope:** `mail.metadata.read`. **Rate limit class:** `read` (600/minute per key).",
        "x-required-scope": "mail.metadata.read",
        "x-rate-limit-class": "read",
        "parameters": [
          {
            "name": "inbox_id",
            "in": "path",
            "required": true,
            "description": "An inbox id from `/v1/inboxes`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "thread_id",
            "in": "path",
            "required": true,
            "description": "`Thread.id`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Thread"
                },
                "examples": {
                  "metadata": {
                    "summary": "Key without mail.content.read",
                    "value": {
                      "id": "18f3a2b4c5d6e7f8",
                      "inbox_id": "5b0c1f8e-2a43-4d7b-9f61-3c2e8a9d4b10",
                      "inbox_email": "jordan@example.com",
                      "owner_email": "jordan@example.com",
                      "category": "Important",
                      "labels": [
                        "INBOX",
                        "IMPORTANT",
                        "SENT"
                      ],
                      "first_message_at": "2026-10-13T16:02:11+00:00",
                      "last_message_at": "2026-10-14T09:15:40+00:00",
                      "todo": {
                        "type": "followup",
                        "urgency": "medium",
                        "due_at": "2026-10-17T16:00:00+00:00",
                        "updated_at": "2026-10-14T09:16:02+00:00"
                      },
                      "message_count": 2,
                      "messages": [
                        {
                          "id": "18f3a2b4c5d6e7f8",
                          "message_id": "<18f3a2b4c5d6e7f8@mail.example.com>",
                          "in_reply_to": null,
                          "date": "2026-10-13T16:02:11+00:00",
                          "received_at": "2026-10-13T16:02:13+00:00",
                          "from": {
                            "email": "pat@customer.example.com",
                            "name": "Pat Lee"
                          },
                          "to": [
                            {
                              "email": "jordan@example.com",
                              "name": "Jordan Kim"
                            }
                          ],
                          "cc": [],
                          "bcc": [],
                          "labels": [
                            "INBOX",
                            "IMPORTANT"
                          ],
                          "is_read": true,
                          "is_starred": false,
                          "is_sent": false,
                          "is_trash": false,
                          "is_spam": false,
                          "size_bytes": 48211,
                          "attachments": [
                            {
                              "mime_type": "application/pdf",
                              "size_bytes": 40960
                            }
                          ]
                        },
                        {
                          "id": "18f3a9d0e1f2a3b4",
                          "message_id": "<18f3a9d0e1f2a3b4@mail.example.com>",
                          "in_reply_to": "<18f3a2b4c5d6e7f8@mail.example.com>",
                          "date": "2026-10-14T09:15:40+00:00",
                          "received_at": "2026-10-14T09:15:40+00:00",
                          "from": {
                            "email": "jordan@example.com",
                            "name": "Jordan Kim"
                          },
                          "to": [
                            {
                              "email": "pat@customer.example.com",
                              "name": "Pat Lee"
                            }
                          ],
                          "cc": [],
                          "bcc": [],
                          "labels": [
                            "SENT"
                          ],
                          "is_read": true,
                          "is_starred": false,
                          "is_sent": true,
                          "is_trash": false,
                          "is_spam": false,
                          "size_bytes": 6120,
                          "attachments": []
                        }
                      ]
                    }
                  },
                  "content": {
                    "summary": "Key with mail.content.read",
                    "value": {
                      "id": "18f3a2b4c5d6e7f8",
                      "inbox_id": "5b0c1f8e-2a43-4d7b-9f61-3c2e8a9d4b10",
                      "inbox_email": "jordan@example.com",
                      "owner_email": "jordan@example.com",
                      "category": "Important",
                      "labels": [
                        "INBOX",
                        "IMPORTANT",
                        "SENT"
                      ],
                      "first_message_at": "2026-10-13T16:02:11+00:00",
                      "last_message_at": "2026-10-14T09:15:40+00:00",
                      "todo": {
                        "type": "followup",
                        "urgency": "medium",
                        "due_at": "2026-10-17T16:00:00+00:00",
                        "updated_at": "2026-10-14T09:16:02+00:00",
                        "summary": "Follow up if Pat hasn't signed the renewal by Friday"
                      },
                      "message_count": 2,
                      "messages": [
                        {
                          "id": "18f3a2b4c5d6e7f8",
                          "message_id": "<18f3a2b4c5d6e7f8@mail.example.com>",
                          "in_reply_to": null,
                          "date": "2026-10-13T16:02:11+00:00",
                          "received_at": "2026-10-13T16:02:13+00:00",
                          "from": {
                            "email": "pat@customer.example.com",
                            "name": "Pat Lee"
                          },
                          "to": [
                            {
                              "email": "jordan@example.com",
                              "name": "Jordan Kim"
                            }
                          ],
                          "cc": [],
                          "bcc": [],
                          "labels": [
                            "INBOX",
                            "IMPORTANT"
                          ],
                          "is_read": true,
                          "is_starred": false,
                          "is_sent": false,
                          "is_trash": false,
                          "is_spam": false,
                          "size_bytes": 48211,
                          "attachments": [
                            {
                              "mime_type": "application/pdf",
                              "size_bytes": 40960,
                              "filename": "Quote-2027.pdf"
                            }
                          ],
                          "subject": "Renewal quote for 2027",
                          "snippet": "Hi Jordan, attached is the signed quote. Can you confirm the start date?"
                        },
                        {
                          "id": "18f3a9d0e1f2a3b4",
                          "message_id": "<18f3a9d0e1f2a3b4@mail.example.com>",
                          "in_reply_to": "<18f3a2b4c5d6e7f8@mail.example.com>",
                          "date": "2026-10-14T09:15:40+00:00",
                          "received_at": "2026-10-14T09:15:40+00:00",
                          "from": {
                            "email": "jordan@example.com",
                            "name": "Jordan Kim"
                          },
                          "to": [
                            {
                              "email": "pat@customer.example.com",
                              "name": "Pat Lee"
                            }
                          ],
                          "cc": [],
                          "bcc": [],
                          "labels": [
                            "SENT"
                          ],
                          "is_read": true,
                          "is_starred": false,
                          "is_sent": true,
                          "is_trash": false,
                          "is_spam": false,
                          "size_bytes": 6120,
                          "attachments": [],
                          "subject": "Re: Renewal quote for 2027",
                          "snippet": "Thanks Pat, confirming November 1. I'll send the invoice this week."
                        }
                      ],
                      "subject": "Renewal quote for 2027"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown, revoked or expired API key. `error` is one of: `invalid_api_key`, `api_key_revoked`, `api_key_expired`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_api_key": {
                    "$ref": "#/components/examples/invalid_api_key"
                  },
                  "api_key_revoked": {
                    "$ref": "#/components/examples/api_key_revoked"
                  },
                  "api_key_expired": {
                    "$ref": "#/components/examples/api_key_expired"
                  }
                }
              }
            },
            "x-error-codes": [
              "invalid_api_key",
              "api_key_revoked",
              "api_key_expired"
            ],
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWWAuthenticate"
              }
            }
          },
          "403": {
            "description": "The key is valid but may not make this call. `error` is one of: `browser_not_allowed`, `ip_not_allowed`, `not_enterprise`, `key_owner_inactive`, `insufficient_scope`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "browser_not_allowed": {
                    "$ref": "#/components/examples/browser_not_allowed"
                  },
                  "ip_not_allowed": {
                    "$ref": "#/components/examples/ip_not_allowed"
                  },
                  "not_enterprise": {
                    "$ref": "#/components/examples/not_enterprise"
                  },
                  "key_owner_inactive": {
                    "$ref": "#/components/examples/key_owner_inactive"
                  },
                  "insufficient_scope": {
                    "summary": "403 insufficient_scope",
                    "value": {
                      "detail": {
                        "error": "insufficient_scope",
                        "message": "This API key does not have the mail.metadata.read scope."
                      }
                    }
                  }
                }
              }
            },
            "x-error-codes": [
              "browser_not_allowed",
              "ip_not_allowed",
              "not_enterprise",
              "key_owner_inactive",
              "insufficient_scope"
            ]
          },
          "404": {
            "description": "Not found, or not readable by this key (the two look the same). `error` is one of: `inbox_not_found`, `thread_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "inbox_not_found": {
                    "$ref": "#/components/examples/inbox_not_found"
                  },
                  "thread_not_found": {
                    "$ref": "#/components/examples/thread_not_found"
                  }
                }
              }
            },
            "x-error-codes": [
              "inbox_not_found",
              "thread_not_found"
            ]
          },
          "422": {
            "description": "A parameter is missing or invalid. The body lists each problem (not the `Error` shape).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                },
                "example": {
                  "detail": [
                    {
                      "type": "uuid_parsing",
                      "loc": [
                        "path",
                        "inbox_id"
                      ],
                      "msg": "Input should be a valid UUID, invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-fA-F-], found `n` at 1",
                      "input": "not-a-uuid",
                      "ctx": {
                        "error": "invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-fA-F-], found `n` at 1"
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait `Retry-After` seconds, then retry. `error` is one of: `too_many_auth_failures`, `rate_limited`, `too_many_concurrent_requests`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "too_many_auth_failures": {
                    "$ref": "#/components/examples/too_many_auth_failures"
                  },
                  "rate_limited": {
                    "summary": "429 rate_limited",
                    "value": {
                      "detail": {
                        "error": "rate_limited",
                        "message": "Rate limit exceeded (600/minute)."
                      }
                    }
                  },
                  "too_many_concurrent_requests": {
                    "$ref": "#/components/examples/too_many_concurrent_requests"
                  }
                }
              }
            },
            "x-error-codes": [
              "too_many_auth_failures",
              "rate_limited",
              "too_many_concurrent_requests"
            ],
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/v1/search": {
      "get": {
        "tags": [
          "Mail"
        ],
        "summary": "Search messages",
        "operationId": "searchMessages",
        "description": "Messages matching a Gmail-style query across the inboxes the key can read, newest first. Returns references; fetch the thread for details. Drafts are never returned.\n\nAny key can use `from:`, `to:`, `cc:`, `bcc:`, `label:`, `is:`, `in:`, `has:`, `after:`, `before:`, `newer_than:`, `older_than:`, `larger:`, `smaller:`, `size:`, `category:` and `todo:`, combined with `OR`, `-` and spaces (AND). Free text, `subject:` and `filename:` need `mail.content.read` (`insufficient_scope` otherwise). `me` (as in `from:me`) and `assigned:` are not supported. A query may have at most 25 parts, counting each term and each AND/OR that joins two (`from:a to:b` is 3).\n\nOffset pagination: pass `next_offset` as `offset` until it is `null`. `offset + limit` may not exceed 10,000; narrow the query to see further.\n\n**Scope:** `mail.metadata.read`. **Rate limit class:** `search` (60/minute per key).",
        "x-required-scope": "mail.metadata.read",
        "x-rate-limit-class": "search",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Gmail search syntax, 1-1000 characters.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 1000
            }
          },
          {
            "name": "inbox_id",
            "in": "query",
            "required": false,
            "description": "Only search these inboxes; repeat the parameter for several. Ids the key can't read are ignored, and if none are readable the result is empty.",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Results per page, 1-100.",
            "schema": {
              "type": "integer",
              "maximum": 100,
              "minimum": 1,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Results to skip. `offset + limit` <= 10,000.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchPage"
                },
                "example": {
                  "results": [
                    {
                      "inbox_id": "5b0c1f8e-2a43-4d7b-9f61-3c2e8a9d4b10",
                      "thread_id": "18f3a2b4c5d6e7f8",
                      "message_id": "18f3a9d0e1f2a3b4",
                      "date": "2026-10-14T09:15:40Z"
                    }
                  ],
                  "next_offset": 50
                }
              }
            }
          },
          "400": {
            "description": "Bad request. `error` is one of: `window_exceeded`, `invalid_query`, `query_too_complex`, `unsupported_operator`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "window_exceeded": {
                    "$ref": "#/components/examples/window_exceeded"
                  },
                  "invalid_query": {
                    "$ref": "#/components/examples/invalid_query"
                  },
                  "query_too_complex": {
                    "$ref": "#/components/examples/query_too_complex"
                  },
                  "unsupported_operator": {
                    "$ref": "#/components/examples/unsupported_operator"
                  }
                }
              }
            },
            "x-error-codes": [
              "window_exceeded",
              "invalid_query",
              "query_too_complex",
              "unsupported_operator"
            ]
          },
          "401": {
            "description": "Missing, malformed, unknown, revoked or expired API key. `error` is one of: `invalid_api_key`, `api_key_revoked`, `api_key_expired`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_api_key": {
                    "$ref": "#/components/examples/invalid_api_key"
                  },
                  "api_key_revoked": {
                    "$ref": "#/components/examples/api_key_revoked"
                  },
                  "api_key_expired": {
                    "$ref": "#/components/examples/api_key_expired"
                  }
                }
              }
            },
            "x-error-codes": [
              "invalid_api_key",
              "api_key_revoked",
              "api_key_expired"
            ],
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWWAuthenticate"
              }
            }
          },
          "403": {
            "description": "The key is valid but may not make this call. `error` is one of: `browser_not_allowed`, `ip_not_allowed`, `not_enterprise`, `key_owner_inactive`, `insufficient_scope`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "browser_not_allowed": {
                    "$ref": "#/components/examples/browser_not_allowed"
                  },
                  "ip_not_allowed": {
                    "$ref": "#/components/examples/ip_not_allowed"
                  },
                  "not_enterprise": {
                    "$ref": "#/components/examples/not_enterprise"
                  },
                  "key_owner_inactive": {
                    "$ref": "#/components/examples/key_owner_inactive"
                  },
                  "insufficient_scope": {
                    "summary": "403 insufficient_scope",
                    "value": {
                      "detail": {
                        "error": "insufficient_scope",
                        "message": "This API key does not have the mail.metadata.read scope."
                      }
                    }
                  }
                }
              }
            },
            "x-error-codes": [
              "browser_not_allowed",
              "ip_not_allowed",
              "not_enterprise",
              "key_owner_inactive",
              "insufficient_scope"
            ]
          },
          "422": {
            "description": "A parameter is missing or invalid. The body lists each problem (not the `Error` shape).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                },
                "example": {
                  "detail": [
                    {
                      "type": "missing",
                      "loc": [
                        "query",
                        "q"
                      ],
                      "msg": "Field required",
                      "input": null
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait `Retry-After` seconds, then retry. `error` is one of: `too_many_auth_failures`, `rate_limited`, `too_many_concurrent_requests`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "too_many_auth_failures": {
                    "$ref": "#/components/examples/too_many_auth_failures"
                  },
                  "rate_limited": {
                    "summary": "429 rate_limited",
                    "value": {
                      "detail": {
                        "error": "rate_limited",
                        "message": "Rate limit exceeded (60/minute)."
                      }
                    }
                  },
                  "too_many_concurrent_requests": {
                    "$ref": "#/components/examples/too_many_concurrent_requests"
                  }
                }
              }
            },
            "x-error-codes": [
              "too_many_auth_failures",
              "rate_limited",
              "too_many_concurrent_requests"
            ],
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable. Retry the same request later. `error` is one of: `search_unavailable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "search_unavailable": {
                    "$ref": "#/components/examples/search_unavailable"
                  }
                }
              }
            },
            "x-error-codes": [
              "search_unavailable"
            ],
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfterOptional"
              }
            }
          }
        }
      }
    },
    "/v1/calendar/events": {
      "get": {
        "tags": [
          "Calendar"
        ],
        "summary": "List calendar events",
        "operationId": "listCalendarEvents",
        "description": "Events between `start` and `end` (at most 31 days apart) on the member's own primary calendar, sorted by start. Recurring series are expanded into occurrences and cancelled events are left out. Calendars the member only subscribes to are not included. Private events are busy time only: `private: true` and no summary, location, organizer or attendees.\n\n**Scope:** `calendar.read`. **Rate limit class:** `calendar` (120/minute per key).",
        "x-required-scope": "calendar.read",
        "x-rate-limit-class": "calendar",
        "parameters": [
          {
            "name": "inbox_id",
            "in": "query",
            "required": true,
            "description": "An inbox id from `/v1/inboxes`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "start",
            "in": "query",
            "required": true,
            "description": "Start of the window, ISO 8601. A time without an offset is read as UTC.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "end",
            "in": "query",
            "required": true,
            "description": "End of the window: after `start`, at most 31 days later.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalendarEventList"
                },
                "example": {
                  "events": [
                    {
                      "id": "4kq9v2m7c1",
                      "calendar_id": "jordan@example.com",
                      "recurring_event_id": null,
                      "start": "2026-10-15T15:00:00+00:00",
                      "end": "2026-10-15T15:30:00+00:00",
                      "all_day": false,
                      "status": "confirmed",
                      "private": false,
                      "summary": "Renewal call",
                      "location": "https://meet.example.com/abc",
                      "organizer_email": "jordan@example.com",
                      "attendees": [
                        {
                          "email": "pat@customer.example.com",
                          "name": "Pat Lee",
                          "response_status": "accepted"
                        }
                      ]
                    },
                    {
                      "id": "7d2s8f1x0p_20261016T170000Z",
                      "calendar_id": "jordan@example.com",
                      "recurring_event_id": "7d2s8f1x0p",
                      "start": "2026-10-16T17:00:00+00:00",
                      "end": "2026-10-16T18:00:00+00:00",
                      "all_day": false,
                      "status": "confirmed",
                      "private": true
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request. `error` is one of: `invalid_window`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_window": {
                    "$ref": "#/components/examples/invalid_window"
                  }
                }
              }
            },
            "x-error-codes": [
              "invalid_window"
            ]
          },
          "401": {
            "description": "Missing, malformed, unknown, revoked or expired API key. `error` is one of: `invalid_api_key`, `api_key_revoked`, `api_key_expired`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_api_key": {
                    "$ref": "#/components/examples/invalid_api_key"
                  },
                  "api_key_revoked": {
                    "$ref": "#/components/examples/api_key_revoked"
                  },
                  "api_key_expired": {
                    "$ref": "#/components/examples/api_key_expired"
                  }
                }
              }
            },
            "x-error-codes": [
              "invalid_api_key",
              "api_key_revoked",
              "api_key_expired"
            ],
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWWAuthenticate"
              }
            }
          },
          "403": {
            "description": "The key is valid but may not make this call. `error` is one of: `browser_not_allowed`, `ip_not_allowed`, `not_enterprise`, `key_owner_inactive`, `insufficient_scope`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "browser_not_allowed": {
                    "$ref": "#/components/examples/browser_not_allowed"
                  },
                  "ip_not_allowed": {
                    "$ref": "#/components/examples/ip_not_allowed"
                  },
                  "not_enterprise": {
                    "$ref": "#/components/examples/not_enterprise"
                  },
                  "key_owner_inactive": {
                    "$ref": "#/components/examples/key_owner_inactive"
                  },
                  "insufficient_scope": {
                    "summary": "403 insufficient_scope",
                    "value": {
                      "detail": {
                        "error": "insufficient_scope",
                        "message": "This API key does not have the calendar.read scope."
                      }
                    }
                  }
                }
              }
            },
            "x-error-codes": [
              "browser_not_allowed",
              "ip_not_allowed",
              "not_enterprise",
              "key_owner_inactive",
              "insufficient_scope"
            ]
          },
          "404": {
            "description": "Not found, or not readable by this key (the two look the same). `error` is one of: `inbox_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "inbox_not_found": {
                    "$ref": "#/components/examples/inbox_not_found"
                  }
                }
              }
            },
            "x-error-codes": [
              "inbox_not_found"
            ]
          },
          "422": {
            "description": "A parameter is missing or invalid. The body lists each problem (not the `Error` shape).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                },
                "example": {
                  "detail": [
                    {
                      "type": "missing",
                      "loc": [
                        "query",
                        "inbox_id"
                      ],
                      "msg": "Field required",
                      "input": null
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait `Retry-After` seconds, then retry. `error` is one of: `too_many_auth_failures`, `rate_limited`, `too_many_concurrent_requests`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "too_many_auth_failures": {
                    "$ref": "#/components/examples/too_many_auth_failures"
                  },
                  "rate_limited": {
                    "summary": "429 rate_limited",
                    "value": {
                      "detail": {
                        "error": "rate_limited",
                        "message": "Rate limit exceeded (120/minute)."
                      }
                    }
                  },
                  "too_many_concurrent_requests": {
                    "$ref": "#/components/examples/too_many_concurrent_requests"
                  }
                }
              }
            },
            "x-error-codes": [
              "too_many_auth_failures",
              "rate_limited",
              "too_many_concurrent_requests"
            ],
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/v1/analytics/team": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "summary": "Get team analytics",
        "operationId": "getTeamAnalytics",
        "description": "Response times, daily message volume and a per-inbox leaderboard: the numbers on the Team Analytics page. Covers the whole team, the same view the key's creator sees, unless the key is limited to certain members; then only their inboxes (`inboxes.team_overall` and ranks still cover the full team). Durations are in seconds.\n\n**Scope:** `analytics.read`. **Rate limit class:** `analytics` (30/minute per key).",
        "x-required-scope": "analytics.read",
        "x-rate-limit-class": "analytics",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "Days in the period, 1-90.",
            "schema": {
              "type": "integer",
              "maximum": 90,
              "minimum": 1,
              "default": 30
            }
          },
          {
            "name": "end_date",
            "in": "query",
            "required": false,
            "description": "Last day included, in `timezone`. Defaults to today.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "timezone",
            "in": "query",
            "required": false,
            "description": "IANA time zone for day boundaries, e.g. `America/Denver`. Defaults to the key creator's Slashy time zone (UTC if unset).",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeamAnalytics"
                },
                "example": {
                  "report": "all",
                  "team": {
                    "id": "0b7d3c52-9e1a-4f6b-8c2d-5a4e3f2b1c09",
                    "name": "Example Co"
                  },
                  "scope": {
                    "inbox_emails": [
                      "jordan@example.com",
                      "sam@example.com"
                    ],
                    "all_team_inboxes": true
                  },
                  "timezone": "America/Denver",
                  "calculation": "calendar",
                  "metric_definitions": {
                    "first_reply_time": "First human reply from the first unanswered incoming message in a conversation episode.",
                    "time_unit": "seconds; null duration means no qualifying replies"
                  },
                  "response_times": {
                    "team_id": "0b7d3c52-9e1a-4f6b-8c2d-5a4e3f2b1c09",
                    "period": {
                      "start": "2026-09-15T06:00:00Z",
                      "end": "2026-10-15T06:00:00Z",
                      "days": 30
                    },
                    "previous_period": {
                      "start": "2026-08-16T06:00:00Z",
                      "end": "2026-09-15T06:00:00Z",
                      "days": 30
                    },
                    "calculation": "calendar",
                    "timezone": "America/Denver",
                    "reply_time": {
                      "average_seconds": 5400.0,
                      "median_seconds": 2700.0,
                      "response_count": 412,
                      "previous_average_seconds": 6000.0,
                      "previous_median_seconds": 3000.0,
                      "previous_response_count": 390,
                      "change_seconds": -600.0,
                      "distribution": [
                        {
                          "key": "under_5m",
                          "label": "Under 5 min",
                          "response_count": 61,
                          "percentage": 0.148,
                          "previous_percentage": 0.13,
                          "change_percentage_points": 1.8
                        }
                      ]
                    },
                    "first_reply_time": {
                      "average_seconds": 5400.0,
                      "median_seconds": 2700.0,
                      "response_count": 412,
                      "previous_average_seconds": 6000.0,
                      "previous_median_seconds": 3000.0,
                      "previous_response_count": 390,
                      "change_seconds": -600.0,
                      "distribution": [
                        {
                          "key": "under_5m",
                          "label": "Under 5 min",
                          "response_count": 61,
                          "percentage": 0.148,
                          "previous_percentage": 0.13,
                          "change_percentage_points": 1.8
                        }
                      ]
                    },
                    "unclassified_response_count": 3,
                    "classification_coverage": 0.99,
                    "computed_at": "2026-10-15T15:00:00Z"
                  },
                  "message_volume": {
                    "team_id": "0b7d3c52-9e1a-4f6b-8c2d-5a4e3f2b1c09",
                    "timezone": "America/Denver",
                    "computed_at": "2026-10-15T15:00:00Z",
                    "current": {
                      "period": {
                        "start": "2026-09-16",
                        "end": "2026-10-15",
                        "days": 30
                      },
                      "buckets": [
                        {
                          "date": "2026-10-15",
                          "received": 214,
                          "sent": 97
                        }
                      ],
                      "received_total": 6120,
                      "sent_total": 2875
                    },
                    "previous": {
                      "period": {
                        "start": "2026-08-17",
                        "end": "2026-09-15",
                        "days": 30
                      },
                      "buckets": [
                        {
                          "date": "2026-09-15",
                          "received": 214,
                          "sent": 97
                        }
                      ],
                      "received_total": 6120,
                      "sent_total": 2875
                    }
                  },
                  "inboxes": {
                    "period": {
                      "start": "2026-09-15T06:00:00Z",
                      "end": "2026-10-15T06:00:00Z",
                      "days": 30
                    },
                    "previous_period": {
                      "start": "2026-08-16T06:00:00Z",
                      "end": "2026-09-15T06:00:00Z",
                      "days": 30
                    },
                    "computed_at": "2026-10-15T15:00:00+00:00",
                    "team_overall": {
                      "first_reply_time": {
                        "median_seconds": 2700.0,
                        "p90_seconds": 21600.0,
                        "response_count": 88,
                        "change_seconds": -300.0
                      },
                      "reply_time": {
                        "median_seconds": 2700.0,
                        "p90_seconds": 21600.0,
                        "response_count": 88,
                        "change_seconds": -300.0
                      },
                      "sent": 2875
                    },
                    "rank_scope": "full_team",
                    "rows": [
                      {
                        "first_reply_time": {
                          "median_seconds": 2700.0,
                          "p90_seconds": 21600.0,
                          "response_count": 88,
                          "change_seconds": -300.0
                        },
                        "reply_time": {
                          "median_seconds": 2700.0,
                          "p90_seconds": 21600.0,
                          "response_count": 88,
                          "change_seconds": -300.0
                        },
                        "sent": 640,
                        "inbox_id": "5b0c1f8e-2a43-4d7b-9f61-3c2e8a9d4b10",
                        "user_id": "7a9c1e3f-5b2d-4c6e-8a0f-1b3d5f7a9c21",
                        "inbox_email": "jordan@example.com",
                        "name": "Jordan Kim",
                        "first_reply_rank": 1
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request. `error` is one of: `invalid_arguments`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_arguments": {
                    "$ref": "#/components/examples/invalid_arguments"
                  }
                }
              }
            },
            "x-error-codes": [
              "invalid_arguments"
            ]
          },
          "401": {
            "description": "Missing, malformed, unknown, revoked or expired API key. `error` is one of: `invalid_api_key`, `api_key_revoked`, `api_key_expired`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_api_key": {
                    "$ref": "#/components/examples/invalid_api_key"
                  },
                  "api_key_revoked": {
                    "$ref": "#/components/examples/api_key_revoked"
                  },
                  "api_key_expired": {
                    "$ref": "#/components/examples/api_key_expired"
                  }
                }
              }
            },
            "x-error-codes": [
              "invalid_api_key",
              "api_key_revoked",
              "api_key_expired"
            ],
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWWAuthenticate"
              }
            }
          },
          "403": {
            "description": "The key is valid but may not make this call. `error` is one of: `browser_not_allowed`, `ip_not_allowed`, `not_enterprise`, `key_owner_inactive`, `insufficient_scope`, `no_readable_inboxes`, `forbidden`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "browser_not_allowed": {
                    "$ref": "#/components/examples/browser_not_allowed"
                  },
                  "ip_not_allowed": {
                    "$ref": "#/components/examples/ip_not_allowed"
                  },
                  "not_enterprise": {
                    "$ref": "#/components/examples/not_enterprise"
                  },
                  "key_owner_inactive": {
                    "$ref": "#/components/examples/key_owner_inactive"
                  },
                  "insufficient_scope": {
                    "summary": "403 insufficient_scope",
                    "value": {
                      "detail": {
                        "error": "insufficient_scope",
                        "message": "This API key does not have the analytics.read scope."
                      }
                    }
                  },
                  "no_readable_inboxes": {
                    "$ref": "#/components/examples/no_readable_inboxes"
                  },
                  "forbidden": {
                    "$ref": "#/components/examples/forbidden"
                  }
                }
              }
            },
            "x-error-codes": [
              "browser_not_allowed",
              "ip_not_allowed",
              "not_enterprise",
              "key_owner_inactive",
              "insufficient_scope",
              "no_readable_inboxes",
              "forbidden"
            ]
          },
          "422": {
            "description": "A parameter is missing or invalid. The body lists each problem (not the `Error` shape).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                },
                "example": {
                  "detail": [
                    {
                      "type": "less_than_equal",
                      "loc": [
                        "query",
                        "days"
                      ],
                      "msg": "Input should be less than or equal to 90",
                      "input": "91",
                      "ctx": {
                        "le": 90
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait `Retry-After` seconds, then retry. `error` is one of: `too_many_auth_failures`, `rate_limited`, `too_many_concurrent_requests`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "too_many_auth_failures": {
                    "$ref": "#/components/examples/too_many_auth_failures"
                  },
                  "rate_limited": {
                    "summary": "429 rate_limited",
                    "value": {
                      "detail": {
                        "error": "rate_limited",
                        "message": "Rate limit exceeded (30/minute)."
                      }
                    }
                  },
                  "too_many_concurrent_requests": {
                    "$ref": "#/components/examples/too_many_concurrent_requests"
                  }
                }
              }
            },
            "x-error-codes": [
              "too_many_auth_failures",
              "rate_limited",
              "too_many_concurrent_requests"
            ],
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable. Retry the same request later. `error` is one of: `invalid_inbox_scope`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_inbox_scope": {
                    "$ref": "#/components/examples/invalid_inbox_scope"
                  }
                }
              }
            },
            "x-error-codes": [
              "invalid_inbox_scope"
            ],
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfterOptional"
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "slsh_live_<id>_<secret>",
        "description": "`Authorization: Bearer slsh_live_<id>_<secret>`. Server-to-server only."
      }
    },
    "headers": {
      "RetryAfter": {
        "required": true,
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "RetryAfterOptional": {
        "description": "Seconds to wait before retrying, when known.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "WWWAuthenticate": {
        "description": "Always `Bearer realm=\"slashy\"`.",
        "schema": {
          "type": "string"
        }
      }
    },
    "examples": {
      "api_key_expired": {
        "summary": "401 api_key_expired",
        "value": {
          "detail": {
            "error": "api_key_expired",
            "message": "This API key has expired."
          }
        }
      },
      "api_key_revoked": {
        "summary": "401 api_key_revoked",
        "value": {
          "detail": {
            "error": "api_key_revoked",
            "message": "This API key has been revoked."
          }
        }
      },
      "browser_not_allowed": {
        "summary": "403 browser_not_allowed",
        "value": {
          "detail": {
            "error": "browser_not_allowed",
            "message": "API keys are for server-to-server calls; browser requests are refused."
          }
        }
      },
      "forbidden": {
        "summary": "403 forbidden",
        "value": {
          "detail": {
            "error": "forbidden",
            "message": "Only team owners/admins can view team analytics."
          }
        }
      },
      "inbox_not_found": {
        "summary": "404 inbox_not_found",
        "value": {
          "detail": {
            "error": "inbox_not_found",
            "message": "No readable inbox with that id."
          }
        }
      },
      "invalid_api_key": {
        "summary": "401 invalid_api_key",
        "value": {
          "detail": {
            "error": "invalid_api_key",
            "message": "Invalid API key."
          }
        }
      },
      "invalid_arguments": {
        "summary": "400 invalid_arguments",
        "value": {
          "detail": {
            "error": "invalid_arguments",
            "message": "1 validation error for TeamMembersQuery\ntimezone\n  Value error, timezone must be a valid IANA timezone"
          }
        }
      },
      "invalid_cursor": {
        "summary": "400 invalid_cursor",
        "value": {
          "detail": {
            "error": "invalid_cursor",
            "message": "cursor must be a next_cursor value returned by this endpoint."
          }
        }
      },
      "invalid_inbox_scope": {
        "summary": "503 invalid_inbox_scope",
        "value": {
          "detail": {
            "error": "invalid_inbox_scope",
            "message": "Unknown team inboxes: ['sam@example.com']; ambiguous team inboxes: []"
          }
        }
      },
      "invalid_query": {
        "summary": "400 invalid_query",
        "value": {
          "detail": {
            "error": "invalid_query",
            "message": "Could not parse the search query."
          }
        }
      },
      "invalid_window": {
        "summary": "400 invalid_window",
        "value": {
          "detail": {
            "error": "invalid_window",
            "message": "end must be after start, at most 31 days later."
          }
        }
      },
      "ip_not_allowed": {
        "summary": "403 ip_not_allowed",
        "value": {
          "detail": {
            "error": "ip_not_allowed",
            "message": "This API key is not allowed from this IP address."
          }
        }
      },
      "key_owner_inactive": {
        "summary": "403 key_owner_inactive",
        "value": {
          "detail": {
            "error": "key_owner_inactive",
            "message": "The person who created this API key is no longer an owner or admin of the team."
          }
        }
      },
      "no_readable_inboxes": {
        "summary": "403 no_readable_inboxes",
        "value": {
          "detail": {
            "error": "no_readable_inboxes",
            "message": "This API key cannot read any of the team's inboxes."
          }
        }
      },
      "not_enterprise": {
        "summary": "403 not_enterprise",
        "value": {
          "detail": {
            "error": "not_enterprise",
            "message": "The public API is only available to enterprise teams."
          }
        }
      },
      "page_unavailable": {
        "summary": "503 page_unavailable",
        "value": {
          "detail": {
            "error": "page_unavailable",
            "message": "This page could not be read. Retry the same request."
          }
        }
      },
      "query_too_complex": {
        "summary": "400 query_too_complex",
        "value": {
          "detail": {
            "error": "query_too_complex",
            "message": "At most 25 search terms and operators."
          }
        }
      },
      "search_unavailable": {
        "summary": "503 search_unavailable",
        "value": {
          "detail": {
            "error": "search_unavailable",
            "message": "Search is temporarily unavailable."
          }
        }
      },
      "thread_not_found": {
        "summary": "404 thread_not_found",
        "value": {
          "detail": {
            "error": "thread_not_found",
            "message": "No thread with that id in this inbox."
          }
        }
      },
      "too_many_auth_failures": {
        "summary": "429 too_many_auth_failures",
        "value": {
          "detail": {
            "error": "too_many_auth_failures",
            "message": "Too many invalid API key attempts."
          }
        }
      },
      "too_many_concurrent_requests": {
        "summary": "429 too_many_concurrent_requests",
        "value": {
          "detail": {
            "error": "too_many_concurrent_requests",
            "message": "At most 4 concurrent requests per API key."
          }
        }
      },
      "unsupported_operator": {
        "summary": "400 unsupported_operator",
        "value": {
          "detail": {
            "error": "unsupported_operator",
            "message": "`me` has no meaning for an API key; use an email address."
          }
        }
      },
      "window_exceeded": {
        "summary": "400 window_exceeded",
        "value": {
          "detail": {
            "error": "window_exceeded",
            "message": "offset + limit may not exceed 10000; narrow the query."
          }
        }
      }
    },
    "schemas": {
      "AnalyticsPeriod": {
        "properties": {
          "start": {
            "format": "date-time",
            "type": "string"
          },
          "end": {
            "format": "date-time",
            "type": "string"
          },
          "days": {
            "type": "integer"
          }
        },
        "required": [
          "start",
          "end",
          "days"
        ],
        "type": "object"
      },
      "Attachment": {
        "type": "object",
        "required": [
          "mime_type",
          "size_bytes"
        ],
        "properties": {
          "mime_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "size_bytes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "filename": {
            "type": [
              "string",
              "null"
            ],
            "description": "Present only when the key has `mail.content.read`.",
            "x-requires-scope": "mail.content.read"
          }
        }
      },
      "Attendee": {
        "type": "object",
        "required": [
          "email",
          "name",
          "response_status"
        ],
        "properties": {
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "response_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "As the calendar provider reports it, e.g. `accepted`, `declined`, `tentative`, `needsAction`."
          }
        }
      },
      "CalendarEvent": {
        "type": "object",
        "required": [
          "id",
          "calendar_id",
          "recurring_event_id",
          "start",
          "end",
          "all_day",
          "status",
          "private"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Event id. A recurring occurrence's id is `<series id>_<start, UTC>`."
          },
          "calendar_id": {
            "type": "string"
          },
          "recurring_event_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The series this occurrence belongs to."
          },
          "start": {
            "type": "string",
            "format": "date-time"
          },
          "end": {
            "type": "string",
            "format": "date-time"
          },
          "all_day": {
            "type": "boolean"
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "description": "e.g. `confirmed` or `tentative`. Cancelled events are left out."
          },
          "private": {
            "type": "boolean",
            "description": "Private events are busy time only: the four fields below are left out."
          },
          "summary": {
            "type": [
              "string",
              "null"
            ],
            "description": "Left out when `private` is true."
          },
          "location": {
            "type": [
              "string",
              "null"
            ],
            "description": "Left out when `private` is true."
          },
          "organizer_email": {
            "type": [
              "string",
              "null"
            ],
            "description": "Left out when `private` is true."
          },
          "attendees": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attendee"
            },
            "description": "Left out when `private` is true."
          }
        },
        "if": {
          "properties": {
            "private": {
              "const": false
            }
          }
        },
        "then": {
          "required": [
            "summary",
            "location",
            "organizer_email",
            "attendees"
          ]
        },
        "else": {
          "not": {
            "anyOf": [
              {
                "required": [
                  "summary"
                ]
              },
              {
                "required": [
                  "location"
                ]
              },
              {
                "required": [
                  "organizer_email"
                ]
              },
              {
                "required": [
                  "attendees"
                ]
              }
            ]
          }
        }
      },
      "CalendarEventList": {
        "type": "object",
        "required": [
          "events"
        ],
        "properties": {
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CalendarEvent"
            },
            "description": "Sorted by start."
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "detail"
        ],
        "properties": {
          "detail": {
            "type": "object",
            "required": [
              "error",
              "message"
            ],
            "properties": {
              "error": {
                "type": "string",
                "description": "Stable machine-readable code. Branch on this."
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation. Wording may change."
              }
            }
          }
        },
        "description": "Every /v1 error except 422 has this shape."
      },
      "HTTPValidationError": {
        "type": "object",
        "required": [
          "detail"
        ],
        "properties": {
          "detail": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            }
          }
        },
        "description": "422 body: one entry per invalid parameter."
      },
      "Inbox": {
        "type": "object",
        "required": [
          "id",
          "email",
          "owner_email"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Pass as `inbox_id` to other endpoints."
          },
          "email": {
            "type": "string",
            "description": "The inbox's address."
          },
          "owner_email": {
            "type": "string",
            "description": "Login email of the member who owns the inbox (lowercase)."
          }
        }
      },
      "InboxList": {
        "type": "object",
        "required": [
          "inboxes"
        ],
        "properties": {
          "inboxes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Inbox"
            }
          }
        }
      },
      "Me": {
        "type": "object",
        "required": [
          "key",
          "team_id",
          "readable_inbox_count"
        ],
        "properties": {
          "key": {
            "type": "object",
            "required": [
              "id",
              "name",
              "scopes",
              "expires_at"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "The key's public id (the part after `slsh_live_`, before the secret).",
                "pattern": "^[a-z2-7]{16}$"
              },
              "name": {
                "type": "string"
              },
              "scopes": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "analytics.read",
                    "calendar.read",
                    "directory.read",
                    "mail.content.read",
                    "mail.metadata.read"
                  ]
                }
              },
              "expires_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "team_id": {
            "type": "string",
            "format": "uuid"
          },
          "readable_inbox_count": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "Member": {
        "type": "object",
        "required": [
          "user_id",
          "email",
          "inboxes"
        ],
        "properties": {
          "user_id": {
            "type": "string",
            "format": "uuid",
            "description": "Slashy user id."
          },
          "email": {
            "type": "string",
            "description": "Login email (lowercase)."
          },
          "inboxes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Inbox"
            }
          }
        }
      },
      "MemberList": {
        "type": "object",
        "required": [
          "members"
        ],
        "properties": {
          "members": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Member"
            },
            "description": "Sorted by email."
          }
        }
      },
      "MemberMetrics": {
        "properties": {
          "first_reply_time": {
            "$ref": "#/components/schemas/MemberResponseStat"
          },
          "reply_time": {
            "$ref": "#/components/schemas/MemberResponseStat"
          },
          "sent": {
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "first_reply_time",
          "reply_time",
          "sent"
        ],
        "type": "object"
      },
      "MemberResponseStat": {
        "properties": {
          "median_seconds": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "p90_seconds": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "response_count": {
            "minimum": 0,
            "type": "integer"
          },
          "change_seconds": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Median change vs the previous equal period"
          }
        },
        "required": [
          "median_seconds",
          "p90_seconds",
          "response_count",
          "change_seconds"
        ],
        "type": "object"
      },
      "Message": {
        "type": "object",
        "required": [
          "id",
          "message_id",
          "in_reply_to",
          "date",
          "received_at",
          "from",
          "to",
          "cc",
          "bcc",
          "labels",
          "is_read",
          "is_starred",
          "is_sent",
          "is_trash",
          "is_spam",
          "size_bytes",
          "attachments"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Provider message id (unique within the inbox)."
          },
          "message_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "RFC 5322 `Message-ID` header."
          },
          "in_reply_to": {
            "type": [
              "string",
              "null"
            ],
            "description": "`In-Reply-To` header."
          },
          "date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it was sent."
          },
          "received_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it was delivered to this inbox."
          },
          "from": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Participant"
              },
              {
                "type": "null"
              }
            ]
          },
          "to": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Participant"
            }
          },
          "cc": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Participant"
            }
          },
          "bcc": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Participant"
            }
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Provider labels. For Gmail these are label ids: `INBOX`, `SENT`, `UNREAD`, `CATEGORY_UPDATES`, `Label_123` and so on."
          },
          "is_read": {
            "type": "boolean"
          },
          "is_starred": {
            "type": "boolean"
          },
          "is_sent": {
            "type": "boolean"
          },
          "is_trash": {
            "type": "boolean"
          },
          "is_spam": {
            "type": "boolean"
          },
          "size_bytes": {
            "type": "integer",
            "minimum": 0
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attachment"
            }
          },
          "subject": {
            "type": "string",
            "description": "Empty string when there is none. Present only when the key has `mail.content.read`.",
            "x-requires-scope": "mail.content.read"
          },
          "snippet": {
            "type": "string",
            "description": "Short preview of the body. Present only when the key has `mail.content.read`.",
            "x-requires-scope": "mail.content.read"
          }
        },
        "description": "A sent or received message. Drafts and scheduled sends are never included."
      },
      "MessageVolumeBucket": {
        "properties": {
          "date": {
            "format": "date",
            "type": "string"
          },
          "received": {
            "minimum": 0,
            "type": "integer"
          },
          "sent": {
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "date",
          "received",
          "sent"
        ],
        "type": "object"
      },
      "MessageVolumePeriod": {
        "properties": {
          "start": {
            "format": "date",
            "type": "string"
          },
          "end": {
            "format": "date",
            "type": "string"
          },
          "days": {
            "type": "integer"
          }
        },
        "required": [
          "start",
          "end",
          "days"
        ],
        "type": "object"
      },
      "MessageVolumeSeries": {
        "properties": {
          "period": {
            "$ref": "#/components/schemas/MessageVolumePeriod"
          },
          "buckets": {
            "items": {
              "$ref": "#/components/schemas/MessageVolumeBucket"
            },
            "type": "array"
          },
          "received_total": {
            "minimum": 0,
            "type": "integer"
          },
          "sent_total": {
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "period",
          "buckets",
          "received_total",
          "sent_total"
        ],
        "type": "object"
      },
      "Participant": {
        "type": "object",
        "required": [
          "email",
          "name"
        ],
        "properties": {
          "email": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ResponseTimeBucket": {
        "properties": {
          "key": {
            "enum": [
              "under_5m",
              "5m_to_30m",
              "30m_to_2h",
              "2h_to_4h",
              "over_4h"
            ],
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "response_count": {
            "minimum": 0,
            "type": "integer"
          },
          "percentage": {
            "maximum": 1,
            "minimum": 0,
            "type": "number"
          },
          "previous_percentage": {
            "maximum": 1,
            "minimum": 0,
            "type": "number"
          },
          "change_percentage_points": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "key",
          "label",
          "response_count",
          "percentage",
          "previous_percentage",
          "change_percentage_points"
        ],
        "type": "object"
      },
      "ResponseTimeMetric": {
        "properties": {
          "average_seconds": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "median_seconds": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "response_count": {
            "type": "integer"
          },
          "previous_average_seconds": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "previous_median_seconds": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "previous_response_count": {
            "type": "integer"
          },
          "change_seconds": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "distribution": {
            "items": {
              "$ref": "#/components/schemas/ResponseTimeBucket"
            },
            "type": "array"
          }
        },
        "required": [
          "average_seconds",
          "median_seconds",
          "response_count",
          "previous_average_seconds",
          "previous_median_seconds",
          "previous_response_count",
          "change_seconds",
          "distribution"
        ],
        "type": "object"
      },
      "SearchPage": {
        "type": "object",
        "required": [
          "results",
          "next_offset"
        ],
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchResult"
            }
          },
          "next_offset": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Pass as `offset` for the next page; `null` when there is none."
          }
        }
      },
      "SearchResult": {
        "type": "object",
        "required": [
          "inbox_id",
          "thread_id",
          "message_id",
          "date"
        ],
        "properties": {
          "inbox_id": {
            "type": "string",
            "format": "uuid"
          },
          "thread_id": {
            "type": "string"
          },
          "message_id": {
            "type": "string",
            "description": "Same as `Message.id`."
          },
          "date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the message was sent."
          }
        }
      },
      "TeamAnalytics": {
        "type": "object",
        "required": [
          "report",
          "team",
          "scope",
          "timezone",
          "calculation",
          "metric_definitions",
          "response_times",
          "message_volume",
          "inboxes"
        ],
        "properties": {
          "report": {
            "type": "string",
            "const": "all"
          },
          "team": {
            "type": "object",
            "required": [
              "id",
              "name"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "scope": {
            "type": "object",
            "required": [
              "inbox_emails",
              "all_team_inboxes"
            ],
            "properties": {
              "inbox_emails": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "all_team_inboxes": {
                "type": "boolean"
              }
            }
          },
          "timezone": {
            "type": "string"
          },
          "calculation": {
            "type": "string",
            "const": "calendar"
          },
          "metric_definitions": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Plain-English definition of each metric."
          },
          "response_times": {
            "$ref": "#/components/schemas/TeamResponseTimes"
          },
          "message_volume": {
            "$ref": "#/components/schemas/TeamMessageVolume"
          },
          "inboxes": {
            "$ref": "#/components/schemas/TeamAnalyticsInboxes"
          }
        },
        "description": "Durations are in seconds; a `null` duration means no qualifying replies."
      },
      "TeamAnalyticsInboxRow": {
        "allOf": [
          {
            "$ref": "#/components/schemas/MemberMetrics"
          },
          {
            "type": "object",
            "required": [
              "inbox_id",
              "user_id",
              "inbox_email",
              "name",
              "first_reply_rank"
            ],
            "properties": {
              "inbox_id": {
                "type": "string",
                "format": "uuid"
              },
              "user_id": {
                "type": "string",
                "format": "uuid"
              },
              "inbox_email": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "first_reply_rank": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 1,
                "description": "Rank by median first reply time across the full team; `null` when the inbox sent no first reply."
              }
            }
          }
        ]
      },
      "TeamAnalyticsInboxes": {
        "type": "object",
        "required": [
          "period",
          "previous_period",
          "computed_at",
          "team_overall",
          "rank_scope",
          "rows"
        ],
        "properties": {
          "period": {
            "$ref": "#/components/schemas/AnalyticsPeriod"
          },
          "previous_period": {
            "$ref": "#/components/schemas/AnalyticsPeriod"
          },
          "computed_at": {
            "type": "string",
            "format": "date-time"
          },
          "team_overall": {
            "$ref": "#/components/schemas/MemberMetrics",
            "description": "Always the full team."
          },
          "rank_scope": {
            "type": "string",
            "const": "full_team"
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TeamAnalyticsInboxRow"
            }
          }
        }
      },
      "TeamMessageVolume": {
        "properties": {
          "team_id": {
            "format": "uuid",
            "type": "string"
          },
          "timezone": {
            "type": "string"
          },
          "current": {
            "$ref": "#/components/schemas/MessageVolumeSeries"
          },
          "previous": {
            "$ref": "#/components/schemas/MessageVolumeSeries"
          },
          "computed_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "team_id",
          "timezone",
          "current",
          "previous",
          "computed_at"
        ],
        "type": "object"
      },
      "TeamResponseTimes": {
        "properties": {
          "team_id": {
            "format": "uuid",
            "type": "string"
          },
          "period": {
            "$ref": "#/components/schemas/AnalyticsPeriod"
          },
          "previous_period": {
            "$ref": "#/components/schemas/AnalyticsPeriod"
          },
          "calculation": {
            "enum": [
              "calendar",
              "business_hours"
            ],
            "type": "string"
          },
          "timezone": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "reply_time": {
            "$ref": "#/components/schemas/ResponseTimeMetric"
          },
          "first_reply_time": {
            "$ref": "#/components/schemas/ResponseTimeMetric"
          },
          "unclassified_response_count": {
            "type": "integer"
          },
          "classification_coverage": {
            "anyOf": [
              {
                "maximum": 1,
                "minimum": 0,
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "computed_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "team_id",
          "period",
          "previous_period",
          "calculation",
          "timezone",
          "reply_time",
          "first_reply_time",
          "unclassified_response_count",
          "classification_coverage",
          "computed_at"
        ],
        "type": "object"
      },
      "Thread": {
        "type": "object",
        "required": [
          "id",
          "inbox_id",
          "inbox_email",
          "owner_email",
          "category",
          "labels",
          "first_message_at",
          "last_message_at",
          "todo",
          "message_count",
          "messages"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Provider thread id (unique within the inbox)."
          },
          "inbox_id": {
            "type": "string",
            "format": "uuid"
          },
          "inbox_email": {
            "type": "string"
          },
          "owner_email": {
            "type": "string"
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": "Slashy category, e.g. `Important` or one of the team's own."
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Union of the messages' labels."
          },
          "first_message_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_message_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "todo": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Todo"
              },
              {
                "type": "null"
              }
            ]
          },
          "message_count": {
            "type": "integer",
            "minimum": 1
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Message"
            },
            "minItems": 1,
            "description": "Oldest first."
          },
          "subject": {
            "type": "string",
            "description": "The first message's subject. Present only when the key has `mail.content.read`.",
            "x-requires-scope": "mail.content.read"
          }
        }
      },
      "ThreadPage": {
        "type": "object",
        "required": [
          "threads",
          "next_cursor"
        ],
        "properties": {
          "threads": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Thread"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` for the next page; `null` on the last page."
          }
        }
      },
      "Todo": {
        "type": "object",
        "required": [
          "type",
          "urgency",
          "due_at",
          "updated_at"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "`reply` (they're waiting on us), `followup` (we're waiting on them) or `dismissed` (reviewed, no action)."
          },
          "urgency": {
            "type": [
              "string",
              "null"
            ],
            "description": "`high`, `medium` or `low`."
          },
          "due_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "summary": {
            "type": [
              "string",
              "null"
            ],
            "description": "One-line description of what needs doing. Present only when the key has `mail.content.read`.",
            "x-requires-scope": "mail.content.read"
          }
        },
        "description": "Slashy's to-do on the thread."
      },
      "ValidationError": {
        "type": "object",
        "required": [
          "type",
          "loc",
          "msg"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "e.g. `missing`, `less_than_equal`, `uuid_parsing`."
          },
          "loc": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "description": "Where the bad value is, e.g. `[\"query\", \"limit\"]`."
          },
          "msg": {
            "type": "string"
          },
          "input": {
            "description": "The value that was sent (`null` when missing)."
          },
          "ctx": {
            "type": "object",
            "description": "Details of the failed constraint, e.g. `{\"le\": 500}`."
          }
        }
      }
    }
  }
}
