# Apple Subscription Source: https://docs.meetjamie.ai/billing-pricing/apple-subscription Learn how to view, change, or cancel your Jamie subscription purchased through the Apple App Store. If you subscribed to Jamie through the Apple App Store, you can manage your subscription directly from your device. Below you'll find step-by-step instructions for both Mac and iPhone. With an Apple subscription, you can only upgrade to the **Personal (Pro & Plus)** plans. If you'd like to switch to a **Team** or **Enterprise** plan, please reach out to us at [support@meetjamie.ai](mailto:support@meetjamie.ai) and we'll help you get set up. ## Manage your subscription You can manage your Jamie subscription directly from the Settings app on your iPhone. Open the **Settings** app on your iPhone. Tap your **name** at the top of the Settings screen to open your Apple Account settings. Tap **Subscriptions**. You'll see a list of all your active and expired subscriptions. Tap the **Jamie** subscription from the list. From here, you can: * **Change your plan** — Select a different option from the available plans. * **Cancel your subscription** — Tap **Cancel Subscription** and confirm. You may need to scroll down to find this button. If there's no Cancel button or you see an expiration message in red text, the subscription is already canceled. On Mac, you manage your subscriptions through **System Settings**. Open the **System Settings** app on your Mac. Click your **name** at the top of the sidebar to open your Apple Account settings. If you don't see your name, click **Sign In** first. Click **Media & Purchases**. Click **Manage** next to **Subscriptions**. Find and select the **Jamie** subscription from the list. From here, you can: * **Change your plan** — Select a different option from the available plans. * **Cancel your subscription** — Click **Cancel Subscription** and confirm. You may need to scroll down to find this button. If there's no Cancel button or you see an expiration message in red text, the subscription is already canceled. ## FAQs No. Apple subscriptions only support upgrades to the Personal (Pro & Plus) plans. If you'd like to move to a Team or Enterprise plan, please contact us at [support@meetjamie.ai](mailto:support@meetjamie.ai). After you cancel, your subscription remains active until the end of your current billing period. You won't be charged again, and you can continue using Jamie until the period expires. Make sure you're signed in with the same Apple Account you used when you originally subscribed. If you're still having trouble, check your email for a receipt from Apple to confirm which account was used. # Invoices Source: https://docs.meetjamie.ai/billing-pricing/invoices How to download your invoices in Jamie. You have recently upgraded your Jamie account to a paid plan, and you would now like to **download your invoice**? You can easily download your invoices (and receipts) by opening our **Billing portal** in your Jamie app and downloading the relevant invoice under “Invoice History”. ### Step by step: 1. Go to your **Settings** 2. Select **Team & Billing** in the settings menu 3. Click on **Billing portal** in the top right 4. Open the relevant invoice under **Invoice History** 5. Click **Download invoice** # Subscriptions & Plans Source: https://docs.meetjamie.ai/billing-pricing/subscriptions-plans How do credits work and what if they are used up? For every meeting recorded with Jamie, one credit is charged, regardless of the duration of the meeting. If meetings are too short for Jamie to create a proper summary, no credit is charged. Below you can see how many credits you have depending on what plan you are on: **0€** Meeting credits: 10 per month Meeting duration limitation: 30m per meeting **25€ per month** Meeting credits: 20 per month Meeting duration limitation: 2h per meeting **47€ per month** Meeting credits: Unlimited per month Meeting duration limitation: 5h per meeting **39€ per member** Meeting credits: Unlimited per month Meeting duration limitation: 5h per meeting **Custom pricing** Meeting credits: Unlimited per month Meeting duration limitation: 5h per meeting Jamie has several plans to fit different needs, from individuals just starting out, to large teams with more meetings. Once you have used all your credits in Jamie, your meeting notes will be locked, and you will need to upgrade to a paid plan to access them. This can either be because you've reached the maximum credits or exceeded the allowed duration limit of your free or paid plan. You can find all detailed information on the different plans on our [Pricing Page](https://www.meetjamie.ai/pricing). If you have more questions on using Jamie with your entire team or organization, then please contact our team at [support@meetjamie.ai](mailto:support@meetjamie.ai). ### FAQs Once you go over the meeting credit limit or extend the duration limit, your summary will be locked. To unlock any meeting summaries for meetings that went over time or credit limit, simply upgrade to one of the higher tier plans. In your settings under billing, you can see how many credits you have on a monthly basis and how many you have already used. Just go to your settings and click on billing. It will show you your current plan, how many credits you have used, and when the plan will be renewed. To cancel your subscription, go to your billing settings and click the Manage button - there is an option to cancel your subscription. Jamie has two custom vocabulary lists that improve transcription accuracy: a personal list (per user) and a team list (shared across your workspace). Each term can be up to 100 characters long. How many terms each list can hold depends on your plan: * **Free, Plus & Pro**: 50 terms per list * **Team**: 100 terms per list * **Enterprise**: 250 terms per list You can manage both lists in your settings under Personalization. You can cancel a Team subscription only after removing all other workspace members. After cancellation, inviting new members is disabled. You’ll stay on the Pro plan for the remainder of the current billing period, at which point your plan will switch to the Free plan. # Access & Security Source: https://docs.meetjamie.ai/developers/api/access-and-security API key scopes, rate limits, and error handling ## API Key Scopes There are two types of API keys. Each key type uses its own set of routes: * **Personal keys** use `/v1/me/` routes * **Workspace keys** use `/v1/workspace/` routes Using a key on the wrong route set will return a `403` error. Both route sets use the same request format — see [Getting Started](/developers/api/getting-started). | | Personal Key | Workspace Key | | ---------------------- | -------------------------------------------------------------------------- | ----------------------------------------- | | **Routes** | `/v1/me/*` | `/v1/workspace/*` | | **Created by** | Any workspace member | Workspace admins only | | **Meetings access** | Your own meetings + meetings shared with you (directly or via shared tags) | All meetings across the workspace | | **Tasks access** | Tasks from your meetings + tasks from shared meetings | All tasks across the workspace | | **Delete meetings** | Your own meetings only | Any meeting in the workspace | | **Search** | Full-text meeting search | Not available | | **Tags** | List your tags | Not available | | **`userEmail` filter** | Not available | Filter results by a specific user's email | | **Rate limit scope** | 100 requests/min per user | 100 requests/min per workspace | | **Max keys** | 3 per user | 10 per workspace | If you only need access to your own data, use a **Personal** key. Use a **Workspace** key when you need to query across all team members or filter by user. ### Shared Meeting Access (Personal Keys) Personal keys don't just return your own meetings — they also include meetings that have been shared with you: * **Directly shared** — Meetings explicitly shared with you by a teammate * **Shared via tags** — Meetings tagged with a label that's been shared with you This applies to both meeting listing and task listing. Deleting meetings is restricted to meetings you own. ## Rate Limiting API requests are limited to **100 requests per minute**. * **Personal keys** — The limit applies per user. Each user with their own key gets their own quota. * **Workspace keys** — The limit applies per workspace. All workspace keys share the same quota. When you exceed the limit, you'll receive a `429 Too Many Requests` response with these headers: | Header | Description | | ----------------------- | ---------------------------------------- | | `X-RateLimit-Limit` | Maximum requests allowed per window | | `X-RateLimit-Remaining` | Requests remaining in the current window | | `X-RateLimit-Reset` | Unix timestamp when the window resets | ## Error Responses All errors return a JSON object with an `error` field: ```json theme={null} { "error": "Description of what went wrong" } ``` ### Status Codes | Status | Meaning | Common Causes | | ------ | ----------------- | ----------------------------------------------------------------------- | | `200` | Success | Request completed successfully | | `400` | Bad Request | Invalid parameters — malformed dates, bad cursor format, missing fields | | `401` | Unauthorized | Missing or invalid API key | | `403` | Forbidden | Wrong key type for route, or insufficient access | | `404` | Not Found | Meeting or resource doesn't exist | | `429` | Too Many Requests | Rate limit exceeded — wait and retry | | `500` | Internal Error | Something went wrong on our end — please retry | ### Common Error Examples **Missing API key:** ```json theme={null} { "error": "Missing API key. Use header: x-api-key: " } ``` **Wrong key type for route:** ```json theme={null} { "error": "This endpoint requires a workspace-scoped API key." } ``` **No access to a meeting:** ```json theme={null} { "error": "Access denied. You do not have access to this meeting." } ``` **Invalid query parameter:** ```json theme={null} { "error": "Invalid startDate format" } ``` # Overview Source: https://docs.meetjamie.ai/developers/api/api Programmatic access to your Jamie meeting data The Jamie API lets you programmatically access your meeting data — summaries, transcripts, tasks, and more. Build custom integrations, sync meeting insights into your tools, or create dashboards tailored to your workflow. ## Base URL ```text theme={null} https://beta-api.meetjamie.ai ``` ## What You Can Do List, fetch, search, and delete meetings Query action items from your meetings List your tags and filter meetings by tag ## Quick Start 1. Make sure you're on a **Pro**, **Team**, or **Enterprise** plan 2. Create an API key in **Settings → Developers → API Keys** 3. Start making requests: ```bash theme={null} # With a workspace key curl -H "x-api-key: jk_your_workspace_key" \ "https://beta-api.meetjamie.ai/v1/workspace/meetings.list" # With a personal key curl -H "x-api-key: jk_your_personal_key" \ "https://beta-api.meetjamie.ai/v1/me/meetings.list" ``` No OAuth flows, no tokens to refresh. Just a single API key in the `x-api-key` header. Use the **Request Builder** in **Settings → Developers → API Keys** to visually construct requests with the right parameters — no manual URL formatting needed. ## Route Structure The API has two route sets — one for each key type. Both use the same request format. ### `/v1/workspace/` — Workspace keys | Method | Endpoint | Description | | ------ | ------------------------------- | --------------------------------------------------------------------- | | `GET` | `/v1/workspace/meetings.list` | [List all workspace meetings](/developers/api/meetings/list-meetings) | | `GET` | `/v1/workspace/meetings.get` | [Get meeting details](/developers/api/meetings/get-meeting) | | `POST` | `/v1/workspace/meetings.delete` | [Delete a meeting](/developers/api/meetings/delete-meeting) | | `GET` | `/v1/workspace/tasks.list` | [List all workspace tasks](/developers/api/tasks/list-tasks) | ### `/v1/me/` — Personal keys | Method | Endpoint | Description | | ------ | ------------------------ | -------------------------------------------------------------------------------- | | `GET` | `/v1/me/meetings.list` | [List your meetings + shared meetings](/developers/api/meetings/list-meetings) | | `GET` | `/v1/me/meetings.get` | [Get meeting details](/developers/api/meetings/get-meeting) | | `POST` | `/v1/me/meetings.delete` | [Delete a meeting you own](/developers/api/meetings/delete-meeting) | | `GET` | `/v1/me/meetings.search` | [Search across your meetings](/developers/api/meetings/search-meetings) | | `GET` | `/v1/me/tasks.list` | [List your tasks + tasks from shared meetings](/developers/api/tasks/list-tasks) | | `GET` | `/v1/me/tags.list` | [List your tags](/developers/api/tags/list-tags) | New to the API? Start with [Getting Started](/developers/api/getting-started) to create your key and make your first request. Then read [Access & Security](/developers/api/access-and-security) to understand key scopes and rate limits. # Getting Started Source: https://docs.meetjamie.ai/developers/api/getting-started Create an API key and make your first request ## Prerequisites * An eligible plan — API access is available on **Pro**, **Team**, and **Enterprise** plans. * An API key — Create one in **Settings → Developers → API Keys**. ## Creating an API Key 1. Navigate to **Settings → Developers → API Keys** 2. Click **Add API Key** 3. If you're an admin, choose the scope: **Personal** or **Workspace** (see [Access & Security](/developers/api/access-and-security) for the difference) 4. Enter a name and optional description 5. Click **Create API Key** 6. **Copy your key immediately** — it won't be shown again Store your API key securely. If you lose it, you'll need to regenerate it from the API key settings. Treat it like a password. ## Authentication Every request must include your API key in the `x-api-key` header. API keys start with the `jk_` prefix. If the key is missing or invalid, you'll receive a `401 Unauthorized` response. ## Making Requests Both key types use the same request format: * **Reading data** uses **GET** with optional parameters via `?input={"json": { ... }}` * **Deleting** uses **POST** with a JSON body * When no parameters are needed, just call the URL directly The only difference is the route prefix: `/v1/workspace/` for workspace keys, `/v1/me/` for personal keys. ### Examples **List meetings (no parameters):** ```bash theme={null} # Workspace key curl -H "x-api-key: jk_your_workspace_key" \ "https://beta-api.meetjamie.ai/v1/workspace/meetings.list" # Personal key curl -H "x-api-key: jk_your_personal_key" \ "https://beta-api.meetjamie.ai/v1/me/meetings.list" ``` **List meetings with filters:** ```bash theme={null} # Workspace key — filter by user and limit curl -H "x-api-key: jk_your_workspace_key" \ 'https://beta-api.meetjamie.ai/v1/workspace/meetings.list?input={"json":{"limit":5,"userEmail":"sarah@example.com"}}' # Personal key — filter by tag curl -H "x-api-key: jk_your_personal_key" \ 'https://beta-api.meetjamie.ai/v1/me/meetings.list?input={"json":{"limit":5,"tag":"Product"}}' ``` **Get a specific meeting:** ```bash theme={null} curl -H "x-api-key: jk_your_key" \ 'https://beta-api.meetjamie.ai/v1/workspace/meetings.get?input={"json":{"meetingId":"YOUR_MEETING_ID"}}' ``` **List open tasks:** ```bash theme={null} curl -H "x-api-key: jk_your_key" \ 'https://beta-api.meetjamie.ai/v1/workspace/tasks.list?input={"json":{"completed":false,"limit":10}}' ``` **Delete a meeting (POST):** ```bash theme={null} curl -X POST -H "x-api-key: jk_your_key" \ -H "Content-Type: application/json" \ -d '{"json": {"meetingId": "YOUR_MEETING_ID"}}' \ "https://beta-api.meetjamie.ai/v1/workspace/meetings.delete" ``` **Search meetings (personal key only):** ```bash theme={null} curl -H "x-api-key: jk_your_personal_key" \ 'https://beta-api.meetjamie.ai/v1/me/meetings.search?input={"json":{"query":"product roadmap"}}' ``` **List your tags (personal key only):** ```bash theme={null} curl -H "x-api-key: jk_your_personal_key" \ "https://beta-api.meetjamie.ai/v1/me/tags.list" ``` **Don't want to build requests by hand?** Use the **Request Builder** in **Settings → Developers → API Keys** to visually select an endpoint, add parameters, and get a ready-to-use cURL command or URL you can copy. **Cheat sheet:** * URL pattern: `/v1/{workspace or me}/{procedure}` (e.g., `/v1/workspace/meetings.list`) * Reading: `GET` with optional `?input={"json": {...}}` * Deleting: `POST` with body `{"json": {...}}` * No parameters? Just call the URL directly ### Response Format All responses wrap data in an envelope: ```json theme={null} { "result": { "data": { "json": { "meetings": [...], "nextCursor": null } } } } ``` Your data is inside `result.data.json`. ## Pagination List endpoints return paginated results. When more data is available, the response includes a `nextCursor` field. Pass it in your next request: ```bash theme={null} GET /v1/workspace/meetings.list?input={"json":{"limit":20,"cursor":"2024-11-27T14:00:00.000Z::7893456789012345678"}} ``` When `nextCursor` is `null`, you've reached the end of the results. # Delete Meeting Source: https://docs.meetjamie.ai/developers/api/meetings/delete-meeting Permanently delete a meeting and all its related data Permanently deletes a meeting and all its related data — including the summary, transcript, and tasks. This action is permanent and cannot be undone. All meeting data including the summary, transcript, and tasks will be permanently removed. ``` POST /v1/workspace/meetings.delete ``` Delete any meeting in the workspace (the meeting owner must be a workspace member). ```bash theme={null} curl -X POST -H "x-api-key: jk_your_workspace_key" \ -H "Content-Type: application/json" \ -d '{"json": {"meetingId": "7893456789012345678"}}' \ "https://beta-api.meetjamie.ai/v1/workspace/meetings.delete" ``` ``` POST /v1/me/meetings.delete ``` Delete meetings you own. You cannot delete meetings that were shared with you. ```bash theme={null} curl -X POST -H "x-api-key: jk_your_personal_key" \ -H "Content-Type: application/json" \ -d '{"json": {"meetingId": "7893456789012345678"}}' \ "https://beta-api.meetjamie.ai/v1/me/meetings.delete" ``` ## Parameters | Parameter | Type | Description | | ----------- | ------ | -------------- | | `meetingId` | string | The meeting ID | ## Example Response ```json theme={null} { "result": { "data": { "json": { "success": true, "message": "Meeting deleted successfully", "deletedMeetingId": "7893456789012345678" } } } } ``` ## Error Responses | Status | Error | Cause | | ------ | ---------------------------------------------------------------- | --------------------------------------------------------- | | `403` | `Access denied. This meeting does not belong to your workspace.` | Workspace key — the meeting owner isn't in your workspace | | `403` | `Access denied. You can only delete your own meetings.` | Personal key — you don't own this meeting | | `404` | `Meeting not found` | No meeting exists with this ID | # Get Meeting Source: https://docs.meetjamie.ai/developers/api/meetings/get-meeting Retrieve full details for a single meeting Returns the full details for a single meeting, including summary, transcript, participants, tasks, tags, and calendar event info. With a personal key, meetings you own also include your scratchpad notes as Markdown. ```text theme={null} GET /v1/workspace/meetings.get ``` Retrieve any meeting in the workspace. ```bash theme={null} curl -H "x-api-key: jk_your_workspace_key" \ 'https://beta-api.meetjamie.ai/v1/workspace/meetings.get?input={"json":{"meetingId":"7893456789012345678"}}' ``` ```text theme={null} GET /v1/me/meetings.get ``` Retrieve meetings you own or that have been shared with you. ```bash theme={null} curl -H "x-api-key: jk_your_personal_key" \ 'https://beta-api.meetjamie.ai/v1/me/meetings.get?input={"json":{"meetingId":"7893456789012345678"}}' ``` ## Parameters | Parameter | Type | Description | | ----------- | ------ | -------------- | | `meetingId` | string | The meeting ID | ## Example Response ```json theme={null} { "result": { "data": { "json": { "id": "7893456789012345678", "title": "Q4 Planning Meeting", "generatedTitle": "Q4 Product Roadmap Review", "startTime": "2024-11-27T14:00:00.000Z", "endTime": "2024-11-27T15:00:00.000Z", "user": { "id": "user-456", "email": "sarah.johnson@example.com" }, "summary": { "markdown": "# Meeting Summary\n\n## Key Discussion Points\n• Reviewed Q4 roadmap...", "html": "

Meeting Summary

Key Discussion Points

", "short": "Q4 product planning meeting covering roadmap priorities and mobile redesign." }, "transcript": "**Sarah Johnson**\nHi everyone! Thanks for joining today's meeting.\n\n**Alex Chen**\nThanks for having me. Let's dive into the agenda.", "scratchpadNotes": "## Follow-up\n\nSend Alex the revised product brief.", "participants": [ { "id": "1", "name": "Sarah Johnson", "email": "sarah.johnson@example.com" }, { "id": "2", "name": "Alex Chen", "email": "alex.chen@example.com" } ], "tasks": [ { "content": "Finalize technical specifications for mobile app redesign", "completed": false, "assignee": { "name": "Alex Chen", "email": "alex.chen@example.com" } } ], "tags": [ { "name": "Product", "color": "#4A90D9" } ], "event": { "id": "calendar-event-123", "externalId": "AAMkAGI1AAAt9AHjAAA=", "title": "Q4 Planning Meeting", "scheduledTime": "2024-11-27T14:00:00.000Z", "endTime": "2024-11-27T15:00:00.000Z", "attendees": [ { "name": "Sarah Johnson", "email": "sarah.johnson@example.com", "responseStatus": "accepted", "organizer": true }, { "name": "Alex Chen", "email": "alex.chen@example.com", "responseStatus": "accepted", "organizer": false } ] } } } } } ``` ## Response Fields ### Top-level | Field | Type | Description | | ---------------- | -------------- | ------------------- | | `id` | string | Unique meeting ID | | `title` | string | Meeting title | | `generatedTitle` | string \| null | AI-generated title | | `startTime` | string | ISO 8601 start time | | `endTime` | string \| null | ISO 8601 end time | ### `user` The Jamie user who recorded the meeting. | Field | Type | Description | | ------- | ------ | ----------- | | `id` | string | User ID | | `email` | string | User email | ### `summary` | Field | Type | Description | | ---------- | ------ | ------------------------------------- | | `markdown` | string | Meeting summary formatted as Markdown | | `html` | string | Meeting summary formatted as HTML | | `short` | string | A brief one-line summary | ### `transcript` The full meeting transcript as a markdown-formatted string. Speaker names are bolded, followed by their spoken text. ### `scratchpadNotes` Your private scratchpad notes as a Markdown-formatted string. This field is returned by `/v1/me/meetings.get` when you own the meeting. It is `null` for meetings shared with you or when the meeting has no scratchpad notes. Workspace-key responses do not include private scratchpad notes. ### `participants` People who actually spoke during the meeting (detected from the transcript). | Field | Type | Description | | ------- | -------------- | ---------------------------- | | `id` | string | Participant ID | | `name` | string | Display name | | `email` | string \| null | Email address (if available) | ### `tasks` Action items extracted from the meeting. | Field | Type | Description | | ---------------- | -------------- | ---------------------------------- | | `content` | string | Task description | | `completed` | boolean | Whether the task is marked as done | | `assignee` | object \| null | Assigned person (if any) | | `assignee.name` | string | Assignee name | | `assignee.email` | string \| null | Assignee email | ### `tags` Tags (labels) applied to the meeting. | Field | Type | Description | | ------- | ------ | --------------------------- | | `name` | string | Tag name | | `color` | string | Hex color (e.g., `#4A90D9`) | ### `event` Calendar event information. Present even if no calendar event is linked (falls back to meeting data). | Field | Type | Description | | ---------------------------- | -------------- | ------------------------------------------------------------------------------ | | `id` | string \| null | Internal Jamie calendar event ID | | `externalId` | string \| null | The calendar provider's event ID (e.g., Microsoft Graph or Google Calendar ID) | | `title` | string | Event title (falls back to meeting title) | | `scheduledTime` | string | ISO 8601 start time | | `endTime` | string \| null | ISO 8601 end time | | `attendees` | array | People invited to the calendar event | | `attendees[].name` | string | Attendee name | | `attendees[].email` | string | Attendee email | | `attendees[].responseStatus` | string \| null | RSVP status: `accepted`, `declined`, `tentative`, or `needsAction` | | `attendees[].organizer` | boolean | Whether this attendee organized the event | **Participants vs. Attendees**: `participants` are people who spoke during the meeting (detected from the transcript). `attendees` are people invited to the calendar event — they may or may not have attended or spoken. # List Meetings Source: https://docs.meetjamie.ai/developers/api/meetings/list-meetings Retrieve a paginated list of meetings Returns a paginated list of meetings, ordered by start time (newest first). ``` GET /v1/workspace/meetings.list ``` Returns all meetings across the workspace. ### Parameters | Parameter | Type | Default | Description | | ----------- | ------ | ------- | ------------------------------------------------------------------- | | `limit` | number | `50` | Number of meetings to return (max `100`) | | `cursor` | string | — | Pagination cursor from a previous response | | `startDate` | string | — | ISO 8601 date. Only return meetings starting on or after this date | | `endDate` | string | — | ISO 8601 date. Only return meetings starting on or before this date | | `userEmail` | string | — | Filter by a specific user's email | ### Example Request ```bash theme={null} curl -H "x-api-key: jk_your_workspace_key" \ 'https://beta-api.meetjamie.ai/v1/workspace/meetings.list?input={"json":{"limit":10,"startDate":"2024-11-01T00:00:00Z"}}' ``` **Filter by user:** ```bash theme={null} curl -H "x-api-key: jk_your_workspace_key" \ 'https://beta-api.meetjamie.ai/v1/workspace/meetings.list?input={"json":{"limit":5,"userEmail":"sarah@example.com"}}' ``` ``` GET /v1/me/meetings.list ``` Returns your own meetings plus meetings shared with you (directly or via shared tags). ### Parameters | Parameter | Type | Default | Description | | ----------- | ------ | ------- | ------------------------------------------------------------------- | | `limit` | number | `50` | Number of meetings to return (max `100`) | | `cursor` | string | — | Pagination cursor from a previous response | | `startDate` | string | — | ISO 8601 date. Only return meetings starting on or after this date | | `endDate` | string | — | ISO 8601 date. Only return meetings starting on or before this date | | `tag` | string | — | Filter by tag name | ### Example Request ```bash theme={null} curl -H "x-api-key: jk_your_personal_key" \ 'https://beta-api.meetjamie.ai/v1/me/meetings.list?input={"json":{"limit":10,"startDate":"2024-11-01T00:00:00Z"}}' ``` **Filter by tag:** ```bash theme={null} curl -H "x-api-key: jk_your_personal_key" \ 'https://beta-api.meetjamie.ai/v1/me/meetings.list?input={"json":{"limit":5,"tag":"Product"}}' ``` ## Example Response ```json theme={null} { "result": { "data": { "json": { "meetings": [ { "id": "7893456789012345678", "title": "Q4 Planning Meeting", "generatedTitle": "Q4 Product Roadmap Review", "startTime": "2024-11-27T14:00:00.000Z", "endTime": "2024-11-27T15:00:00.000Z", "calendarEventId": "calendar-event-123", "userId": "user-456" } ], "nextCursor": "2024-11-20T10:00:00.000Z::7893456789012345677" } } } } ``` ## Response Fields | Field | Type | Description | | ---------------------------- | -------------- | ------------------------------------------------------ | | `meetings` | array | List of meeting objects | | `meetings[].id` | string | Unique meeting ID | | `meetings[].title` | string | Meeting title | | `meetings[].generatedTitle` | string \| null | AI-generated title | | `meetings[].startTime` | string | ISO 8601 start time | | `meetings[].endTime` | string \| null | ISO 8601 end time | | `meetings[].calendarEventId` | string \| null | Linked calendar event ID | | `meetings[].userId` | string | ID of the user who recorded the meeting | | `nextCursor` | string \| null | Cursor for the next page, or `null` if no more results | # Search Meetings Source: https://docs.meetjamie.ai/developers/api/meetings/search-meetings Semantic search across your meeting content ``` GET /v1/me/meetings.search ``` Searches across your meetings using semantic similarity matching. Returns matching text chunks with their source meeting. Only available with a **personal** API key. Results are drawn from meetings within the last 6 months by default, returning up to 40 matches ranked by relevance. ## Parameters | Parameter | Type | Description | | ----------- | ------ | ------------------------------------------------------------------- | | `query` | string | **Required.** The search query | | `startDate` | string | ISO 8601 date. Only search meetings starting on or after this date | | `endDate` | string | ISO 8601 date. Only search meetings starting on or before this date | ## Example Request ```bash theme={null} curl -H "x-api-key: jk_your_personal_key" \ 'https://beta-api.meetjamie.ai/v1/me/meetings.search?input={"json":{"query":"product roadmap"}}' ``` ## Example Response ```json theme={null} { "result": { "data": { "json": { "results": [ { "id": "chunk-001", "text": "We need to finalize the product roadmap for Q1 before the holidays...", "meetingId": "7893456789012345678", "meetingTitle": "Q4 Planning Meeting", "meetingDate": "2024-11-27" }, { "id": "chunk-002", "text": "The roadmap priorities should focus on mobile redesign and API improvements...", "meetingId": "7893456789012345679", "meetingTitle": "Product Sync", "meetingDate": "2024-11-20" } ] } } } } ``` ## Response Fields | Field | Type | Description | | ------------------------ | -------------- | ------------------------------------------------- | | `results` | array | List of matching text chunks, ranked by relevance | | `results[].id` | string | Unique chunk ID | | `results[].text` | string | The matching text excerpt | | `results[].meetingId` | string | ID of the source meeting | | `results[].meetingTitle` | string \| null | Title of the source meeting | | `results[].meetingDate` | string | Date of the meeting (`YYYY-MM-DD`) | ## Common Patterns **Search with a date range:** ```bash theme={null} curl -H "x-api-key: jk_your_personal_key" \ 'https://beta-api.meetjamie.ai/v1/me/meetings.search?input={"json":{"query":"hiring plan","startDate":"2024-10-01T00:00:00Z","endDate":"2024-12-01T00:00:00Z"}}' ``` This endpoint is only available with a personal API key. It searches across your own meetings and meetings shared with you. # List Tags Source: https://docs.meetjamie.ai/developers/api/tags/list-tags Retrieve all tags available to you ``` GET /v1/me/tags.list ``` Returns all tags (labels) that you own or that have been shared with you. Only available with a **personal** API key. No parameters required. ## Example Request ```bash theme={null} curl -H "x-api-key: jk_your_personal_key" \ "https://beta-api.meetjamie.ai/v1/me/tags.list" ``` ## Example Response ```json theme={null} { "result": { "data": { "json": { "tags": [ { "id": "tag-001", "name": "Product", "shared": false }, { "id": "tag-002", "name": "Engineering", "shared": true } ] } } } } ``` ## Response Fields | Field | Type | Description | | --------------- | ------- | ----------------------------------------------------------------------------- | | `tags` | array | List of tag objects | | `tags[].id` | string | Unique tag ID | | `tags[].name` | string | Tag name | | `tags[].shared` | boolean | `true` if this tag was shared with you by another user, `false` if you own it | Use the tag name with the `tag` parameter on [`/v1/me/meetings.list`](/developers/api/meetings/list-meetings) to filter meetings by tag. # List Tasks Source: https://docs.meetjamie.ai/developers/api/tasks/list-tasks Retrieve action items extracted from your meetings Returns a paginated list of action items extracted from meetings, ordered by creation date (newest first). ``` GET /v1/workspace/tasks.list ``` Returns all tasks across the workspace. ### Parameters | Parameter | Type | Default | Description | | ----------- | ------- | ------- | --------------------------------------------------------------- | | `limit` | number | `50` | Number of tasks to return (max `100`) | | `cursor` | string | — | Pagination cursor from a previous response | | `startDate` | string | — | ISO 8601 date. Only return tasks created on or after this date | | `endDate` | string | — | ISO 8601 date. Only return tasks created on or before this date | | `userEmail` | string | — | Filter by a specific user's email | | `completed` | boolean | — | Filter by completion status (`true` or `false`) | | `meetingId` | string | — | Filter tasks from a specific meeting | ### Example Request ```bash theme={null} curl -H "x-api-key: jk_your_workspace_key" \ 'https://beta-api.meetjamie.ai/v1/workspace/tasks.list?input={"json":{"completed":false,"limit":20}}' ``` ``` GET /v1/me/tasks.list ``` Returns tasks from your own meetings and meetings shared with you. ### Parameters | Parameter | Type | Default | Description | | ----------- | ------- | ------- | --------------------------------------------------------------- | | `limit` | number | `50` | Number of tasks to return (max `100`) | | `cursor` | string | — | Pagination cursor from a previous response | | `startDate` | string | — | ISO 8601 date. Only return tasks created on or after this date | | `endDate` | string | — | ISO 8601 date. Only return tasks created on or before this date | | `completed` | boolean | — | Filter by completion status (`true` or `false`) | | `meetingId` | string | — | Filter tasks from a specific meeting | ### Example Request ```bash theme={null} curl -H "x-api-key: jk_your_personal_key" \ 'https://beta-api.meetjamie.ai/v1/me/tasks.list?input={"json":{"completed":false,"limit":20}}' ``` ## Example Response ```json theme={null} { "result": { "data": { "json": { "tasks": [ { "id": "8901234567890123456", "text": "Finalize technical specifications for mobile app redesign", "completed": false, "assignee": { "id": "person-789", "name": "Alex Chen", "email": "alex.chen@example.com" }, "meetingId": "7893456789012345678", "meetingTitle": "Q4 Planning Meeting", "createdAt": "2024-11-27T15:00:00.000Z", "userId": "user-456" }, { "id": "8901234567890123457", "text": "Schedule follow-up meeting for next week", "completed": true, "assignee": null, "meetingId": "7893456789012345678", "meetingTitle": "Q4 Planning Meeting", "createdAt": "2024-11-27T15:00:00.000Z", "userId": "user-456" } ], "nextCursor": "2024-11-20T10:00:00.000Z" } } } } ``` ## Response Fields | Field | Type | Description | | ------------------------ | -------------- | ------------------------------------------------------ | | `tasks` | array | List of task objects | | `tasks[].id` | string | Unique task ID | | `tasks[].text` | string | Task description | | `tasks[].completed` | boolean | Whether the task is marked as done | | `tasks[].assignee` | object \| null | Assigned person (if any) | | `tasks[].assignee.id` | string \| null | Assignee person ID | | `tasks[].assignee.name` | string | Assignee name | | `tasks[].assignee.email` | string \| null | Assignee email | | `tasks[].meetingId` | string | ID of the meeting this task was extracted from | | `tasks[].meetingTitle` | string \| null | Title of the source meeting | | `tasks[].createdAt` | string | ISO 8601 timestamp when the task was created | | `tasks[].userId` | string | ID of the user who recorded the meeting | | `nextCursor` | string \| null | Cursor for the next page, or `null` if no more results | ## Common Patterns **Get all open tasks:** ```bash theme={null} curl -H "x-api-key: jk_your_key" \ 'https://beta-api.meetjamie.ai/v1/workspace/tasks.list?input={"json":{"completed":false}}' ``` **Get tasks from a specific meeting:** ```bash theme={null} curl -H "x-api-key: jk_your_key" \ 'https://beta-api.meetjamie.ai/v1/workspace/tasks.list?input={"json":{"meetingId":"7893456789012345678"}}' ``` **Get tasks from the last 7 days:** ```bash theme={null} curl -H "x-api-key: jk_your_key" \ 'https://beta-api.meetjamie.ai/v1/workspace/tasks.list?input={"json":{"startDate":"2024-11-20T00:00:00Z"}}' ``` # Delivery & Retries Source: https://docs.meetjamie.ai/developers/webhooks/delivery-retries How webhook deliveries work & how to test them ### Delivery Expectations * **Timeout**: Webhook requests time out after **30 seconds** * **Success**: Your endpoint should return a 2xx HTTP status code to acknowledge receipt * **Response Body**: The response body is not processed (you can return any content or leave it empty) ### Retry Behavior If your endpoint doesn't respond with a 2xx status code, Jamie automatically retries the delivery with exponential backoff: | Attempt | Delay | Total Time Elapsed | | :------ | :--------- | :----------------- | | 1 | Immediate | 0s | | 2 | 10 seconds | 10s | | 3 | 60 seconds | 1m 10s | | 4 | 10 minutes | 11m 10s | | 5 | 1 hour | 1h 11m 10s | After 5 failed attempts, the webhook delivery is marked as failed and: * The webhook delivery is moved to a dead-letter queue * For **personal webhooks**, the webhook owner receives an email notification about the failure * For **workspace webhooks**, all workspace administrators receive an email notification about the failure * Email notifications are rate-limited to **1 per hour per webhook endpoint** to prevent spam ### Failure Scenarios A webhook delivery fails and triggers a retry if: * Your endpoint returns a non-2xx HTTP status code * Your endpoint doesn't respond within 30 seconds * A network error occurs during delivery A webhook delivery is **skipped** (no retries) if: * The webhook endpoint has been deleted * The webhook endpoint is disabled/inactive ## Testing Webhooks You can send a test webhook to verify your endpoint is working correctly: 1. Go to **Settings → Integrations → Webhooks** 2. Find the webhook you want to test 3. Click the **Test** button 4. Check your endpoint to confirm it received the test payload Test webhooks: * Send a realistic mock payload with sample meeting data * Use the same retry and delivery mechanism as real webhooks * Require the webhook to be **enabled/active** * Count toward rate limits and retry attempts Use test webhooks during development to verify your signature verification and payload processing logic before going live. # Getting Started Source: https://docs.meetjamie.ai/developers/webhooks/getting-started All you need to know to get started ### Prerequisites Before setting up a webhook, you'll need: 1. **An HTTPS endpoint** - Your webhook receiver must use HTTPS (HTTP is not supported for security reasons) 2. **Workspace membership** - Any workspace member can create personal webhooks. Workspace admin access is required to create workspace-wide webhooks. 3. **An eligible plan** - Upgrade to Plus or higher to access webhooks ### Creating a Webhook 1. Navigate to **Settings → Integrations → Webhooks** 2. Click **Create Webhook** 3. Fill in the required information: * **URL**: Your HTTPS endpoint that will receive webhook events (e.g., `https://api.example.com/webhooks/jamie`) * **Description** (optional): A note to help you identify this webhook * **Events**: Select which events should trigger this webhook (currently only `meeting.completed`) 4. **Choose the webhook scope:** * **Personal webhook** — Triggers only for your own meetings. Available to all workspace members. * **Workspace webhook** — Triggers for all meetings in the workspace. Requires admin access. 5. **Choose your authentication method:** The simplest way to authenticate webhooks. Jamie will send a static API key in a header of your choice. * **Header Name**: Choose a custom header name (e.g., `x-jamie-api-key`, `x-make-apikey`, `Authorization`) * Leave empty to use the default `x-jamie-api-key` To verify requests, simply compare the header value with your stored secret. See [Security & Verification](/developers/webhooks/security-verification) for more implementation details. For maximum security, Jamie signs each request with HMAC-SHA256. This verifies both authenticity and payload integrity. The signature is sent in the `x-jamie-signature` header. See [Security & Verification](/developers/webhooks/security-verification) for implementation details. 6. Click **Create** 7. **Save your secret key** - This is displayed only once. Store it securely (e.g., in your environment variables or secrets manager). Once you close the webhook creation dialog, you won't be able to see the secret key again. If you lose it, you'll need to regenerate it from the webhook settings. ## Events Jamie currently supports the following webhook event: ### `meeting.completed` Triggered when a meeting has been fully processed and is ready with: * Meeting summary (in Markdown and HTML formats) * Full transcript * Extracted action items/tasks * Participant information * Meeting metadata **Workspace webhooks** are triggered by all meetings of every user in the workspace. **Personal webhooks** are triggered only by the creator's own meetings. If you need to receive webhooks for only your meetings, use a personal webhook instead of filtering on the receiving end. ## Payload Structure When a webhook event is triggered, Jamie sends a POST request to your endpoint with the following structure: ### Headers Depending on your chosen authentication method, you'll receive different headers: **API Key Authentication:** ```text theme={null} Content-Type: application/json user-agent: Jamie-Webhooks/1.0 x-jamie-api-key: sk_a1b2c3d4e5f6... (or your custom header name) jamie-event: meeting.completed jamie-delivery: 7893456789012345678 ``` **Signature Verification:** ```text theme={null} Content-Type: application/json user-agent: Jamie-Webhooks/1.0 x-jamie-signature: t=1234567890,v0=5f3d8a7b2c1e9f4a6b8c0d2e4f6a8b0c jamie-event: meeting.completed jamie-delivery: 7893456789012345678 ``` | Header | Description | | :------------------ | :----------------------------------------------------------------------------------------------------------------------------------- | | `x-jamie-api-key` | Your API key for simple authentication (or your custom header name) | | `x-jamie-signature` | HMAC-SHA256 signature for verifying webhook authenticity (see [Security & Verification](/developers/webhooks/security-verification)) | | `jamie-event` | The event type that triggered this webhook | | `jamie-delivery` | Unique identifier for this webhook delivery | ### Request Body ```json theme={null} { "metadata": { "id": "7893456789012345678", "event": "meeting.completed", "created": 1701234567 }, "data": { "title": "Q4 Planning Meeting", "startTime": "2024-11-27T14:00:00.000Z", "endTime": "2024-11-27T15:00:00.000Z", "user": { "id": "user-456", "email": "sarah.johnson@example.com" }, "summary": { "markdown": "# Meeting Summary\n\n## Key Discussion Points\n• Reviewed Q4 roadmap...", "html": "

Meeting Summary

Key Discussion Points

", "short": "Q4 product planning meeting covering roadmap, mobile redesign, and user research budget allocation." }, "transcript": [ { "speakerId": "1", "speakerName": "Sarah Johnson", "text": "Hi everyone! Thanks for joining today's meeting." }, { "speakerId": "2", "speakerName": "Alex Chen", "text": "Thanks for having me. Let's dive into the agenda." } ], "participants": [ { "id": "1", "name": "Sarah Johnson", "email": "sarah.johnson@example.com" }, { "id": "2", "name": "Alex Chen", "email": "alex.chen@example.com" } ], "event": { "id": "calendar-event-123", "externalId": "AAMkAGI1AAAt9AHjAAA=", "title": "Q4 Planning Meeting", "scheduledTime": "2024-11-27T14:00:00.000Z", "endTime": "2024-11-27T15:00:00.000Z", "attendees": [ { "name": "Sarah Johnson", "email": "sarah.johnson@example.com", "responseStatus": "accepted", "organizer": true }, { "name": "Alex Chen", "email": "alex.chen@example.com", "responseStatus": "accepted", "organizer": false }, { "name": "Jamie Martinez", "email": "jamie.martinez@example.com", "responseStatus": "tentative", "organizer": false } ] }, "tasks": [ { "content": "Finalize technical specifications for mobile app redesign", "completed": false, "assignee": { "name": "Alex Chen", "email": "alex.chen@example.com" } }, { "content": "Schedule follow-up meeting for next week", "completed": false, "assignee": null } ], "tags": [ { "name": "Product", "color": "#4A90D9" }, { "name": "Planning", "color": "#50C878" } ] } } ``` ### Field Descriptions #### `metadata` * `id` (string): Unique identifier for this webhook delivery * `event` (string): The event type (`meeting.completed`) * `created` (number): Unix timestamp (seconds) when the webhook was created #### `data` (top-level) * `title` (string): Meeting title * `startTime` (string): ISO 8601 formatted start time of the meeting * `endTime` (string | null): ISO 8601 formatted end time of the meeting (if available) #### `data.user` The Jamie user who recorded the meeting: * `id` (string): Unique identifier for the user * `email` (string): Email address of the user #### `data.summary` * `markdown` (string): Meeting summary formatted as Markdown with speaker names highlighted * `html` (string): Meeting summary formatted as HTML, ready to display * `short` (string): A brief one-line summary of the meeting #### `data.transcript` Array of transcript segments: * `speakerId` (string): Unique identifier for the speaker * `speakerName` (string): Display name of the speaker * `text` (string): The spoken text #### `data.participants` Array of meeting participants detected in the transcript (people who spoke): * `id` (string): Unique identifier for the participant * `name` (string): Display name of the participant * `email` (string | null): Email address of the participant (if available) #### `data.event` Calendar event information (if the meeting is linked to a calendar event): * `id` (string | null): Internal Jamie calendar event ID (if available) * `externalId` (string | null): The calendar provider's event ID (e.g., Microsoft Graph API event ID or Google Calendar event ID). Use this to match against events in your own calendar system. * `title` (string): Calendar event title (falls back to meeting title if no calendar event is linked) * `scheduledTime` (string): ISO 8601 formatted start time * `endTime` (string | null): ISO 8601 formatted end time (if available) * `attendees` (array): List of people invited to the calendar event (empty if no calendar event is linked) * `name` (string): Name of the attendee * `email` (string): Email address of the attendee * `responseStatus` (string | null): RSVP status — one of `needsAction`, `declined`, `tentative`, or `accepted` * `organizer` (boolean): Whether this attendee is the organizer of the event **Participants vs. Attendees:** `participants` are people who actually **spoke** during the meeting (detected from the transcript). `attendees` are people who were **invited** to the calendar event — they may or may not have attended or spoken. #### `data.tasks` Array of action items extracted from the meeting: * `content` (string): The task description * `completed` (boolean): Whether the task is marked as completed * `assignee` (object | null): The person assigned to this task (if any) * `name` (string | null): Name of the assignee * `email` (string | null): Email address of the assignee (if available) #### `data.tags` Array of tags (labels) applied to the meeting: * `name` (string): The tag name * `color` (string): The tag color as a hex string (e.g., `#4A90D9`) # Managing Webhooks Source: https://docs.meetjamie.ai/developers/webhooks/maintenance Change webhooks to your needs ### Updating a Webhook 1. Navigate to **Settings → Integrations → Webhooks** 2. Click the webhook you want to edit 3. Update the URL, description, or toggle the active status 4. Click **Save** You can update: * **URL**: Change where webhook events are delivered * **Description**: Update the identifying note * **Active Status**: Enable or disable webhook delivery without deleting the endpoint You cannot update the selected events after creation. If you need different events, create a new webhook endpoint. ### Regenerating the Signing Secret If your signing secret is compromised or lost: 1. Navigate to **Settings → Integrations → Webhooks** 2. Click the webhook 3. Click **Regenerate Secret** 4. Save the new secret immediately - it will only be shown once 5. Update your webhook receiver with the new secret Regenerating the signing secret immediately invalidates the old one. Any webhooks using the old secret will fail verification. Update your receiving endpoint before regenerating. ### Deleting a Webhook 1. Navigate to **Settings → Integrations → Webhooks** 2. Click the webhook you want to delete 3. Click **Delete** 4. Confirm the deletion Once deleted: * The webhook stops receiving events immediately * Any pending deliveries for this webhook are cancelled * The signing secret is permanently invalidated * This action cannot be undone # Make.com Integration Source: https://docs.meetjamie.ai/developers/webhooks/make-com-integration Step-by-step guide to integrate Jamie webhooks with Make.com This guide walks you through setting up a Jamie webhook that sends meeting summaries to [Make.com](https://make.com) for automation. ### Prerequisites * A Jamie account with Plus plan or higher * A Make.com account * Workspace membership in Jamie (admin access required only for workspace-wide webhooks) ### Step 1: Create the Webhook in Jamie First We'll create the Jamie webhook first to get the API key, then use it when setting up Make.com. 1. Go to **Settings → Integrations → Webhooks** in Jamie 2. Click **Create Webhook** 3. Enter a placeholder URL for now (e.g., `https://hook.make.com/placeholder`) 4. Add a description (e.g., "Make.com automation") 5. Click **Continue** 6. Select **API Key** as the authentication method 7. Set the **Header Name** to: `x-make-apikey` 8. Click **Create Webhook** 9. **Copy the generated secret key** (`sk_...`) - you'll need this for Make.com Save this key somewhere safe! You won't be able to see it again. ### Step 2: Create the Webhook in Make.com Now create the Make.com webhook with API key verification built-in: 1. Log in to your Make.com account 2. Create a new scenario or open an existing one 3. Click **+** to add a new module 4. Search for **Webhooks** and select **Custom webhook** 5. Click **Add** to create a new webhook 6. Enter a **Webhook name** (e.g., "Jamie webhook") 7. Expand **API Key authentication** → **API keys** 8. Click **+ Add API key** 9. Click **Add** next to the "Choose a key" dropdown 10. In the **Add new keychain** dialog: * **Name**: Enter a name (e.g., "Jamie API Key") * **API key value**: Paste the secret key (`sk_...`) from Jamie 11. Click **Create** 12. Select the key you just created from the dropdown 13. Click **Save** 14. **Copy the webhook URL** that Make.com generates The URL will look something like: `https://hook.make.com/abc123xyz789...` Make.com will now automatically reject any requests that don't include the correct `x-make-apikey` header with your secret key. ### Step 3: Update the Jamie Webhook URL 1. Go back to Jamie **Settings → Integrations → Webhooks** 2. Find your webhook and click to edit it 3. Replace the placeholder URL with the real Make.com webhook URL you copied 4. Click **Save** ### Step 4: Determine the Data Structure 1. In Make.com, right-click on your webhook module and select **Run this module only** 2. Go to Jamie and click **Send Test Webhook** for your webhook 3. Make.com will capture the test data and show you the available fields 4. Click **OK** to save the structure This step is important! Make.com needs to "learn" the data structure before you can map fields to other modules. ### Step 5: Build Your Automation Now you can use the meeting data in your Make.com scenario! Here are some common examples: #### Send Summary to Slack ``` Webhook → Slack: Create a Message ``` Map the fields: * **Channel**: Select your channel (e.g., `#meeting-summaries`) * **Text**: Click and select `data` → `summary` → `markdown` #### Save to Google Docs ``` Webhook → Google Docs: Create a Document ``` Map the fields: * **Title**: `{{data.event.title}} - Meeting Summary` * **Content**: Select `data` → `summary` → `html` #### Add Tasks to Notion ``` Webhook → Iterator → Notion: Create a Database Item ``` 1. Add an **Iterator** module after the webhook 2. Set the array to: `data` → `tasks` 3. Connect to Notion and map: * **Task name**: `content` (from the iterator) * **Status**: `To Do` ### Example: Complete Automation Flow ``` Jamie Webhook ↓ Slack: Post summary to #meetings ↓ Iterator: Loop through tasks ↓ Notion: Create task items ↓ Google Calendar: Create follow-up event ``` ### Webhook Data Available in Make.com After the webhook receives data, you'll have access to these fields: | Field Path | Description | Example | | :------------------------- | :--------------------------- | :------------------------------ | | `metadata.id` | Webhook delivery ID | `"7893456789012345678"` | | `metadata.event` | Event type | `"meeting.completed"` | | `data.event.title` | Meeting title | `"Q4 Planning Meeting"` | | `data.event.scheduledTime` | Start time (ISO 8601) | `"2024-11-27T14:00:00.000Z"` | | `data.event.endTime` | End time (ISO 8601) | `"2024-11-27T15:00:00.000Z"` | | `data.summary.markdown` | Summary in Markdown | `"# Meeting Summary..."` | | `data.summary.html` | Summary in HTML | `"

Meeting Summary

..."` | | `data.transcript[]` | Array of transcript segments | See below | | `data.participants[]` | Array of participants | `[{ id, name }]` | | `data.tasks[]` | Array of action items | `[{ content, completed }]` | #### Transcript Structure Each item in `data.transcript[]`: * `speakerId`: Unique speaker identifier * `speakerName`: Display name * `text`: What was said #### Tasks Structure Each item in `data.tasks[]`: * `content`: The task description * `completed`: Boolean (usually `false` for new tasks) ### Troubleshooting **Webhook not receiving data:** * Ensure the webhook is enabled in Jamie (check the toggle) * Verify the Make.com URL is correct (no extra spaces) * Check that your Jamie plan supports webhooks * Make sure the Make.com scenario is **turned on** (not just saved) **Authentication failing (401 error):** * Verify the API key in Make.com matches exactly the key from Jamie * Check that the header name in Jamie is set to `x-make-apikey` * Try regenerating the key in Jamie and updating it in Make.com **"No data" in Make.com:** * Run the webhook module in "Run once" mode first * Send a test webhook from Jamie to capture the structure * Check the Make.com **History** tab for incoming requests **Fields not mapping correctly:** * Re-run the "determine data structure" step * Make sure you're selecting fields from the correct module * Check that array fields use an Iterator module **Scenario not running automatically:** * Verify the scenario is **turned on** (toggle in bottom-left) * Check your Make.com plan limits (operations per month) * Look for errors in the scenario's execution history Use Make.com's **History** tab to inspect incoming webhook payloads and debug any issues. You can see the full request including headers and body. # Security & Verification Source: https://docs.meetjamie.ai/developers/webhooks/security-verification How to set up security Jamie offers two authentication methods for webhooks. Choose the one that best fits your needs. ## Authentication Methods ### API Key (Recommended) The simplest way to verify webhook requests. Jamie sends a static API key in a header that you specify during webhook creation. **How it works:** 1. When creating the webhook, choose "API Key" authentication 2. Optionally specify a custom header name (default: `x-jamie-api-key`) 3. Save the generated secret key (`sk_...`) 4. In your webhook handler, compare the header value with your stored secret #### Verification Code Examples ```javascript theme={null} const express = require('express'); const app = express(); app.use(express.json()); app.post('/webhooks/jamie', (req, res) => { // Get the API key from header (use your custom header name if configured) const apiKey = req.headers['x-jamie-api-key']; // Compare with your stored secret if (apiKey !== process.env.JAMIE_WEBHOOK_SECRET) { console.error('Invalid API key'); return res.status(401).send('Unauthorized'); } // API key is valid, process the webhook const { data } = req.body; console.log('Meeting completed:', data.event.title); console.log('Summary:', data.summary.markdown); // Process tasks data.tasks.forEach(task => { console.log('Task:', task.content); }); res.status(200).send('OK'); }); app.listen(3000, () => { console.log('Webhook server running on port 3000'); }); ``` ```python theme={null} import os from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/webhooks/jamie', methods=['POST']) def handle_webhook(): # Get the API key from header (use your custom header name if configured) api_key = request.headers.get('x-jamie-api-key') # Compare with your stored secret if api_key != os.environ.get('JAMIE_WEBHOOK_SECRET'): print('Invalid API key') return 'Unauthorized', 401 # API key is valid, process the webhook data = request.get_json() print('Meeting completed:', data['data']['event']['title']) print('Summary:', data['data']['summary']['markdown']) # Process tasks for task in data['data']['tasks']: print('Task:', task['content']) return 'OK', 200 if __name__ == '__main__': app.run(port=3000) ``` ```go theme={null} package main import ( "encoding/json" "fmt" "io" "net/http" "os" ) type WebhookPayload struct { Data struct { Event struct { Title string `json:"title"` } `json:"event"` Summary struct { Markdown string `json:"markdown"` } `json:"summary"` Tasks []struct { Content string `json:"content"` } `json:"tasks"` } `json:"data"` } func webhookHandler(w http.ResponseWriter, r *http.Request) { // Get the API key from header (use your custom header name if configured) apiKey := r.Header.Get("x-jamie-api-key") // Compare with your stored secret if apiKey != os.Getenv("JAMIE_WEBHOOK_SECRET") { fmt.Println("Invalid API key") http.Error(w, "Unauthorized", http.StatusUnauthorized) return } // API key is valid, process the webhook body, _ := io.ReadAll(r.Body) var payload WebhookPayload json.Unmarshal(body, &payload) fmt.Println("Meeting completed:", payload.Data.Event.Title) fmt.Println("Summary:", payload.Data.Summary.Markdown) // Process tasks for _, task := range payload.Data.Tasks { fmt.Println("Task:", task.Content) } w.WriteHeader(http.StatusOK) } func main() { http.HandleFunc("/webhooks/jamie", webhookHandler) fmt.Println("Webhook server running on port 3000") http.ListenAndServe(":3000", nil) } ``` ### Signature Verification (Advanced) For maximum security, Jamie can sign all webhook requests using HMAC-SHA256. This ensures requests are authentic **and** haven't been tampered with. #### Signature Format The `x-jamie-signature` header uses the following format: ``` t=,v0= ``` * `t`: Unix timestamp (seconds) when the signature was created * `v0`: HMAC-SHA256 signature of the payload #### Verification Steps 1. **Extract the timestamp and signature** from the `x-jamie-signature` header 2. **Validate the timestamp** - Reject requests with timestamps older than 5 minutes (300 seconds) to prevent replay attacks 3. **Recreate the signed message** - Concatenate the timestamp, a period (`.`), and the raw request body: `{timestamp}.{raw_body}` 4. **Compute the expected signature** - Generate an HMAC-SHA256 hash of the message using your signing secret 5. **Compare signatures** - Use a constant-time comparison to prevent timing attacks #### Verification Code Examples ```javascript theme={null} const crypto = require('crypto'); async function verifyWebhookSignature( payload: string, signature: string, secret: string ): Promise { // Parse signature header: t=timestamp,v0=hash const parts = signature.split(','); const timestamp = parts[0].split('=')[1]; const expectedSignature = parts[1].split('=')[1]; // Validate timestamp (prevent replay attacks - 5 min tolerance) const now = Math.floor(Date.now() / 1000); if (now - parseInt(timestamp) > 300) { throw new Error('Signature expired'); } // Recreate the signed message const message = `${timestamp}.${payload}`; // Generate HMAC signature const hmac = crypto.createHmac('sha256', secret); hmac.update(message); const actualSignature = hmac.digest('hex'); // Compare signatures using constant-time comparison const expectedBuffer = Buffer.from(expectedSignature, 'hex'); const actualBuffer = Buffer.from(actualSignature, 'hex'); if (!crypto.timingSafeEqual(expectedBuffer, actualBuffer)) { throw new Error('Invalid signature'); } return true; } // Usage in your webhook endpoint (Express example): app.post('/webhooks/jamie', async (req, res) => { const signature = req.headers['x-jamie-signature']; const payload = JSON.stringify(req.body); try { await verifyWebhookSignature( payload, signature, process.env.JAMIE_WEBHOOK_SECRET ); // Signature is valid, process the webhook const { data } = req.body; console.log('Meeting completed:', data.event.title); res.status(200).send('OK'); } catch (error) { console.error('Webhook verification failed:', error); res.status(401).send('Unauthorized'); } }); ``` ```python theme={null} import hmac import hashlib import time import json from typing import Dict def verify_webhook_signature( payload: str, signature: str, secret: str ) -> bool: """Verify Jamie webhook signature Args: payload: The raw request body as a string signature: The x-jamie-signature header value secret: Your webhook signing secret Returns: True if signature is valid Raises: ValueError: If signature is invalid or expired """ # Parse signature header: t=timestamp,v0=hash parts: Dict[str, str] = { p.split('=')[0]: p.split('=')[1] for p in signature.split(',') } timestamp: int = int(parts['t']) expected_signature: str = parts['v0'] # Validate timestamp (prevent replay attacks - 5 min tolerance) now: int = int(time.time()) if now - timestamp > 300: raise ValueError('Signature expired') # Recreate the signed message message: str = f"{timestamp}.{payload}" # Generate HMAC signature signature_bytes: str = hmac.new( secret.encode('utf-8'), message.encode('utf-8'), hashlib.sha256 ).hexdigest() # Compare signatures (constant-time comparison) if not hmac.compare_digest(signature_bytes, expected_signature): raise ValueError('Invalid signature') return True # Usage in your webhook endpoint (Flask example): from flask import Flask, request app = Flask(__name__) @app.route('/webhooks/jamie', methods=['POST']) def handle_webhook(): signature: str = request.headers.get('x-jamie-signature') payload: str = request.get_data(as_text=True) try: verify_webhook_signature( payload, signature, os.environ['JAMIE_WEBHOOK_SECRET'] ) # Signature is valid, process the webhook data: Dict = json.loads(payload) print('Meeting completed:', data['data']['event']['title']) return 'OK', 200 except Exception as e: print('Webhook verification failed:', str(e)) return 'Unauthorized', 401 ``` ```go theme={null} package main import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "io" "net/http" "os" "strconv" "strings" "time" ) // verifyWebhookSignature verifies the Jamie webhook signature func verifyWebhookSignature(payload, signature, secret string) error { // Parse signature header: t=timestamp,v0=hash parts := make(map[string]string) for _, part := range strings.Split(signature, ",") { kv := strings.SplitN(part, "=", 2) if len(kv) == 2 { parts[kv[0]] = kv[1] } } timestamp, err := strconv.ParseInt(parts["t"], 10, 64) if err != nil { return fmt.Errorf("invalid timestamp: %w", err) } expectedSignature := parts["v0"] // Validate timestamp (prevent replay attacks - 5 min tolerance) now := time.Now().Unix() if now-timestamp > 300 { return fmt.Errorf("signature expired") } // Recreate the signed message message := fmt.Sprintf("%d.%s", timestamp, payload) // Generate HMAC signature h := hmac.New(sha256.New, []byte(secret)) h.Write([]byte(message)) actualSignature := hex.EncodeToString(h.Sum(nil)) // Compare signatures (constant-time comparison) if !hmac.Equal([]byte(actualSignature), []byte(expectedSignature)) { return fmt.Errorf("invalid signature") } return nil } // WebhookPayload represents the structure of Jamie webhook data type WebhookPayload struct { Metadata struct { ID string `json:"id"` Event string `json:"event"` Created int64 `json:"created"` } `json:"metadata"` Data struct { Event struct { Title string `json:"title"` } `json:"event"` } `json:"data"` } // webhookHandler handles incoming webhooks from Jamie func webhookHandler(w http.ResponseWriter, r *http.Request) { signature := r.Header.Get("x-jamie-signature") body, err := io.ReadAll(r.Body) if err != nil { http.Error(w, "Failed to read body", http.StatusBadRequest) return } secret := os.Getenv("JAMIE_WEBHOOK_SECRET") if err := verifyWebhookSignature( string(body), signature, secret, ); err != nil { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } // Signature is valid, process the webhook var payload WebhookPayload if err := json.Unmarshal(body, &payload); err != nil { http.Error(w, "Invalid JSON", http.StatusBadRequest) return } fmt.Printf("Meeting completed: %s\n", payload.Data.Event.Title) w.WriteHeader(http.StatusOK) } func main() { http.HandleFunc("/webhooks/jamie", webhookHandler) http.ListenAndServe(":8080", nil) } ``` **Security Best Practices:** * Always verify the API key or signature before processing webhook data * For signature verification, use constant-time comparison to prevent timing attacks * Validate timestamps to prevent replay attacks (recommended tolerance: 5 minutes) * Store your secret key securely (use environment variables or a secrets manager) * Never commit secret keys to version control # Troubleshooting Source: https://docs.meetjamie.ai/developers/webhooks/troubleshooting Guidelines to troubleshoot ### Webhook deliveries are failing **Check your endpoint:** * Ensure it's accessible from the internet (not localhost or behind a firewall) * Verify it uses HTTPS (HTTP is not supported) * Confirm it returns a 2xx status code * Make sure it responds within 30 seconds **Check your signature verification:** * Ensure you're using the correct signing secret * Verify you're comparing the raw request body (not parsed JSON) * Check that timestamp validation isn't too strict (recommended: 5 minutes) **Check the webhook status:** * Ensure the webhook is enabled/active * Verify you haven't exceeded the webhook limit (3 personal webhooks per user, or 5 workspace webhooks per workspace) ### Not receiving webhooks **Verify the webhook configuration:** * Check that the webhook is enabled/active * Confirm the URL is correct * Ensure you've selected the `meeting.completed` event **Check your plan:** * Webhooks require Plus, Pro, Team, or Enterprise plans * Free plans don't have access to webhooks **Verify meetings are completing:** * Webhooks only fire when meetings are fully processed * Check that meetings are being recorded and processed successfully **Check webhook scope:** * **Personal webhooks** only fire for the creator's own meetings — they won't trigger for other users' meetings * **Workspace webhooks** fire for all meetings in the workspace ### Receiving duplicate webhooks This is expected behavior and your endpoint should be **idempotent**: * Use the `metadata.id` field to track which deliveries you've processed * If you receive the same `metadata.id` twice, skip processing the duplicate Duplicates can occur due to: * Retries after temporary failures * Network issues causing multiple delivery attempts # Overview Source: https://docs.meetjamie.ai/developers/webhooks/webhooks Set up webhooks to customize your workflow with Jamie ## Overview Webhooks allow you to receive real-time notifications when events occur in Jamie. When a meeting is completed and processed, Jamie can automatically send the meeting data (summary, transcript, tasks, and more) to your specified endpoint. This enables you to: * **Integrate meeting data into your systems** - Automatically sync meeting summaries to your CRM, project management tools, or internal databases * **Build custom workflows** - Trigger actions based on meeting completions, such as creating tickets, sending notifications, or updating records * **Maintain data consistency** - Keep your systems up-to-date with the latest meeting information without manual exports ## Availability Webhooks are available on the following plans: * **Pro** * **Team** * **Enterprise** Jamie supports two types of webhooks: * **Personal webhooks** — Created by any workspace member. They only trigger for the creator's own meetings. Each user can create up to **3 personal webhooks**. * **Workspace webhooks** — Created by workspace admins. They trigger for all meetings across the workspace. Each workspace can have up to **5 workspace webhooks**. All workspace members can access the Developers page to manage their personal webhooks. Only admins can create and manage workspace-wide webhooks. ## Rate Limits * **Maximum personal webhooks per user**: 3 * **Maximum workspace webhooks per workspace**: 5 * **Failure notification emails**: 1 per hour per webhook endpoint * **Webhook timeout**: 30 seconds * **Maximum retries**: 5 attempts ## Support If you're experiencing issues with webhooks: 1. Check the [Troubleshooting](/developers/webhooks/troubleshooting) section 2. Review webhook delivery logs in your Jamie workspace 3. Contact support with your webhook endpoint ID and delivery ID for assistance For feature requests or feedback about webhooks, please reach out to our support team. # Firewall Ports Source: https://docs.meetjamie.ai/enterprise/admins/firewall-ports The endpoints and ports the Jamie desktop app needs to reach. All Jamie connections are **outbound only** (client → server) over **port 443/TCP**. No inbound rules and no UDP are required. **Minimal configuration:** allowlisting `*.meetjamie.ai` plus `turn.cloudflare.com` covers the core services. The remaining endpoints depend on which login method, calendar, and integrations you use. ## Core services Required for Jamie to work. | Endpoint | Protocol | Purpose | | --------------------- | --------- | ----------------------------------- | | `app.meetjamie.ai` | HTTPS/WSS | Web application & API | | `api.meetjamie.ai` | HTTPS/WSS | Backend API | | `turn.meetjamie.ai` | TLS | WebRTC audio streaming (TURN relay) | | `turn.cloudflare.com` | TLS | TURN fallback | ## Authentication Depending on your login method. | Endpoint | Protocol | Purpose | | --------------------------- | -------- | ------------------------- | | `appleid.apple.com` | HTTPS | Apple Sign-In | | `accounts.google.com` | HTTPS | Google Sign-In | | `oauth2.googleapis.com` | HTTPS | Google OAuth token | | `login.microsoftonline.com` | HTTPS | Microsoft / Entra Sign-In | ## Calendar integration Optional — only needed if you connect a calendar. | Endpoint | Protocol | Purpose | | --------------------- | -------- | ----------------------------- | | `graph.microsoft.com` | HTTPS | Microsoft Calendar & Contacts | | `www.googleapis.com` | HTTPS | Google Calendar API | ## Telemetry & updates | Endpoint | Protocol | Purpose | | ----------------------- | -------- | ------------------- | | `eu.i.posthog.com` | HTTPS | Analytics (EU) | | `*.ingest.de.sentry.io` | HTTPS | Error tracking (EU) | | `api.github.com` | HTTPS | Update check | | `github.com` | HTTPS | Update download | ## Notes * Only outbound connections on port 443/TCP. * No UDP required (TURN-over-TLS). * Minimal configuration: `*.meetjamie.ai` + `turn.cloudflare.com`. # Integrations & Developers Source: https://docs.meetjamie.ai/enterprise/admins/integrations-and-developers Connect Jamie to the tools your team already uses — CRMs, docs, and task tools — and build on top of it with the API, webhooks, and MCP. **What's available:** * **CRM:** HubSpot, Salesforce, Attio (log meetings to contacts, companies, deals, accounts) — plus DealCloud and Affinity for deal teams. * **Note-taking:** Notion, Google Docs, OneNote. * **Task management:** Asana (turn action items into tasks). **Connecting one:** Go to **Settings → Integrations**. Pick the integration → click **Connect** → complete the authorization flow → set your preferences. **Two ways to send meetings:** * **Manual:** click **Share** on a meeting → **Integrations** tab → choose where to send it. * **Automatic sync:** turn on auto-sync in the integration's settings to push every new meeting automatically. **Choose what syncs:** Summary (AI notes), Transcript (full word-for-word), and Tasks (action items). Not every integration supports all three — check the individual integration page. **Managing them:** in **Settings → Integrations** you can see connection status, configure what content syncs, and disconnect any integration. **Need a custom workflow?** If you need an integration or automation that isn't listed (CRM mapping, an internal system, a specific data flow), reach out to your Account Executive. ## API, Webhooks & MCP For teams that want to build on top of Jamie, there are three developer surfaces. Programmatic access to your meeting data (summaries, transcripts, tasks, tags). * Create a key in **Settings → Developers → API Keys**, then authenticate with a single `x-api-key` header — no OAuth, no token refresh. * Two key types: **workspace keys** (`/v1/workspace/…` — all workspace meetings/tasks) and **personal keys** (`/v1/me/…` — your own + shared meetings, plus search and tags). * Base URL: `https://beta-api.meetjamie.ai`. Use the built-in **Request Builder** to construct calls visually. Get notified automatically when events happen (e.g. a meeting finishes processing), so you can trigger downstream workflows instead of polling. Includes delivery/retry handling, signature verification for security, and a ready-made Make.com guide. Set them up in the developer settings. Connect AI tools (Claude, ChatGPT, Cursor, Windsurf, or your own) directly to your meeting notes so they can search meetings, pull summaries, and extract action items. * Server URL: `https://mcp.meetjamie.ai/mcp` (Streamable HTTP + browser-based OAuth; no API keys or client secrets needed). * Available tools: `list_meetings`, `get_meeting`, `search_meetings`, `list_tasks`, `create_tasks`, `list_tags`, `list_templates`, `upsert_template`. * **Admin note:** on Claude Team/Enterprise, an Owner must first add the connector in **Organization settings → Connectors** before members can enable it. Browse all available integrations. API, webhooks, and MCP reference. # Intune MSI Deployment Guide Source: https://docs.meetjamie.ai/enterprise/admins/intune-deployment-guide Deploy Jamie as a per-machine Line-of-Business MSI app through Microsoft Intune. ## Overview Jamie is deployed as a **Line-of-Business (LOB) MSI app** via Microsoft Intune. The MSI performs a **per-machine installation** (`InstallScope="perMachine"`), installing into `C:\Program Files\Jamie` for all users on the device. Jamie includes a built-in auto-updater (`JamieUpdaterService`), so Intune should only handle the initial deployment — not ongoing version management. ## Download Jamie\_5.6.52\_x64\_en-US.msi The MSI supports **x64** and **ARM64** architectures. 32-bit (x86) builds are not supported. ## Adding the app in Intune Navigate to **Microsoft Intune admin center → Apps → All apps → Add**. Choose **Line-of-business app**. Upload the `.msi` file. ### App information | Setting | Value | | ---------------------- | -------------------------------------- | | Name | Jamie | | Publisher | Jamie (Wespond UG) | | App Install Context | Device (pre-selected, per-machine MSI) | | **Ignore app version** | **Yes** | | Command-line arguments | `/qn` | ### Ignore app version — why this matters The **Ignore app version** setting must be set to **Yes**. You'll find it in the **App information** pane when configuring the LOB app. Jamie ships with a built-in update service (`JamieUpdaterService`) that automatically updates the application in the background. If **Ignore app version** is left at **No** (the default), Intune enforces the originally deployed MSI version, which creates a race condition: Setting **Ignore app version = Yes** tells Intune to only verify that Jamie is installed, without enforcing a specific version. This is the Microsoft-recommended approach for MSI apps with self-updating installers. ## What the MSI installs | Component | Location / Details | | ------------------------------ | ---------------------------------------------------------------------------- | | Main executables and resources | `C:\Program Files\Jamie\*` | | Registry | `HKLM\Software\Jamie (Wespond UG)\Jamie` — stores `InstallDir` and `Version` | | Deep link protocol | `jamie://` (registered in `HKLM\Software\Classes`) | | Shortcuts | Desktop + Start Menu | **MSI UpgradeCode:** `7f2a9f62-56a0-4f67-9e14-1c18553b1b60` ## Assignments Assign the app to the appropriate device groups as **Required** for automatic deployment, or **Available for enrolled devices** if users should install it from Company Portal. Since this is a per-machine install, target **device groups** rather than user groups for the most predictable behavior. ## Uninstall The MSI supports standard uninstall via Intune. The `JamieUpdaterService` is automatically stopped and removed on uninstall. All shortcuts, registry entries, and the install directory are cleaned up. ## Notes * **Do not** use Intune app supersedence with auto-update enabled for Jamie — the built-in updater handles all version management. * The MSI uses `REINSTALLMODE=amus` and `AllowSameVersionUpgrades="yes"`, so re-deploying the same version is safe and non-destructive. * The WebView2 runtime is bundled and bootstrapped by the installer if it isn't already present on the device. # Setup Essentials Source: https://docs.meetjamie.ai/enterprise/admins/setup-essentials The core of your rollout — logins, calendar access, activating users, driving adoption, the recording notice, and installing Jamie. ## Single Sign-On (SSO / SAML) SSO lets your organization manage authentication centrally through your identity provider (IdP). Jamie supports SAML 2.0, compatible with Okta, Microsoft Entra ID, Google Workspace, Auth0, ADP, Cloudflare, and any standard SAML or OIDC provider. Setup is two steps, in order, both under **Settings → Security** in the Jamie app. 1. Go to **Settings → Security** and click **Configure** next to **Domain Verification Settings**. 2. Enter your domain (e.g. `example.com`). 3. Add the provided TXT record to your DNS (Cloudflare, Route 53, Google Domains, etc.). 4. Verification is automatic — usually under a minute, but up to 48 hours if you're editing an existing TXT record. Repeat for multiple domains (e.g. a parent and subsidiary). Domain verification settings 1. Go to **Settings → Security** and click **Configure** next to **SSO-Provider Connection Settings**. 2. Select your IdP and follow the on-screen wizard. 3. You'll get an **ACS URL** and **Entity ID** to enter in your IdP; the wizard has step-by-step instructions per provider. Supported IdPs: Okta, Microsoft Entra ID (Azure AD), Google Workspace, Auth0, ADP, Cloudflare, Custom SAML (any SAML 2.0 IdP), and Custom OIDC. SSO provider selection After setup, anyone signing in with a verified domain is **required** to authenticate through your IdP. Users who previously used email or social login get redirected to SSO on their next login. **Directory Sync (SCIM)** — available on Enterprise. Automatically create, update, and deprovision users from your IdP under **Settings → Security → Directory Connection Settings**. See [Directory Sync](/enterprise/directory-sync) for setup instructions. **IP allowlisting** is available on Enterprise. Restrict access to your workspace to approved IP ranges under **Settings → Security → IP Allowlist**. See [IP allowlisting](/enterprise/ip-allowlisting) for setup instructions. ## Calendar permissions Connecting a calendar is what makes most of Jamie's smart features work: it detects upcoming meetings, sends start-recording reminders, auto-fills meeting titles, improves speaker identification, and it's a hard requirement for the [automated recording notice](#recording-notification) and other upcoming features. Each user connects their own calendar — there is no org-wide calendar connection. **How users connect** (Settings → Integrations): * **Google Calendar** — Integrations → Google Calendar → **Connect Google Calendar** → sign in → grant permissions. * **Outlook** — Integrations → Outlook → **Connect Outlook Calendar** → sign in → grant permissions. Multiple calendars can be selected and synced individually. **What you as an admin should know:** If your org requires admin consent for third-party apps, users will hit an "Approval required" screen. Approve calendar access once for everyone with Jamie's approval link: any Jamie user can copy it from **Settings → Integrations → Outlook Calendar**. Open it as a Microsoft 365 administrator, review the six sign-in and calendar permissions, and accept. After that, users connect without individual approval. Avoid the generic **Grant admin consent** button in the Entra admin center. It approves every permission registered on the Jamie application, not just the ones the calendar integration uses. See [Outlook calendar](/getting-started/calendar/outlook#for-administrators) for the exact permission list. Jamie uses the Microsoft Graph API, which only reaches mailboxes hosted in Exchange Online. On-prem or un-migrated hybrid mailboxes will see a "mailbox is hosted on-premise" error. Options are migrating the mailbox to Exchange Online, or exploring Exchange Web Services with support. **Permissions Jamie requests:** | Permission | Purpose | | ----------------------------------------- | -------------------------------------------------- | | `openid`, `profile`, `email`, `User.Read` | Secure sign-in and basic profile | | `Calendars.Read` | Detect meetings, reminders, auto-titles | | `People.Read`, `Contacts.Read` | Speaker identification / Speaker Memory | | `offline_access` | Keep the calendar synced without repeated sign-ins | Data stored from the calendar: event details (title, time, location, description, conferencing links), attendee names/emails, and contact info for speaker memory. `People.Read` / `Contacts.Read` power **Speaker Memory**, which creates voiceprints classed as **biometric data** under GDPR. Jamie can keep this deactivated by default at workspace level on request — the recommended default for most deployments. ## Activating users How users get access depends on your setup: * **With SSO:** anyone signing in with a verified domain is automatically routed through your IdP. Control exactly who gets in by assigning users or groups to the Jamie app in your identity provider (Entra ID, Okta, Google Workspace, etc.). Remove them there to revoke access. * **Calendar approval:** make sure you've granted admin consent (see [Calendar permissions](#calendar-permissions)) so users can connect calendars without hitting an approval wall. ## Driving real adoption Getting licenses assigned is the easy part — getting people to actually use Jamie is what matters. What tends to work: * **Make calendar connection step one.** Most of Jamie's value (reminders, auto-titles, speaker ID) depends on it. Push users to connect on day one. * **Seed shared templates and tags.** If teams open Jamie and find ready-made formats for their meeting types, they adopt faster than from a blank slate. * **Tie it to an existing habit** — e.g. "every client call gets a Jamie summary shared in the deal channel." * **Set up integrations early** (CRM, Notion, Slack) so notes flow into tools people already live in, instead of being one more place to check. * **Revisit usage after a couple of months** to see how it's going, where there are questions, etc. Want usage visibility? If you want to know who's actively using Jamie across the org, ask your Account Manager. ## Recording notification As the customer you are the **data controller**, so you're responsible for the legal basis (typically legitimate interest for routine business meetings) and for informing participants under GDPR Arts. 13 and 14. Jamie gives you several mechanisms to meet that transparency obligation. **Automated recording notice:** * Sends attendees an email \~24 hours before each meeting explaining you'll use Jamie. * Requires a connected calendar. * Attendees from your own workspace are auto-excluded, and domain exclusion can be configured for internal-only deployments. * Enable it under **Settings → Notifications → Recording notice**. Recording notice settings **Other mechanisms you can combine:** * A configurable notice inside the calendar invite or in your signature. * A quick verbal heads-up at the start of the meeting. ## Intune guide The app your users download from [meetjamie.ai](https://www.meetjamie.ai/) is a **per-user MSI installer** — the right choice for almost every deployment. * **No admin rights needed.** Each user installs Jamie under their own profile, so it works even in locked-down corporate environments without local admin. * **Automatic updates.** Jamie updates itself in the background — no admin privileges required. * **Works with Intune too.** You can distribute the per-user MSI through Intune as a user-context (rather than device-context) assignment if you prefer a managed rollout. If your organization runs an **application blocker** (e.g. AppLocker), you'll need to allow Jamie to install and update — see [Common deployment issues & troubleshooting](/enterprise/admins/troubleshooting#app-blocker-prevents-installation). ### Need a per-machine installation? Some enterprises prefer a single **per-machine** installation shared by all users on a device. This option is available on request — reach out to your dedicated **[Account Manager](/enterprise/admins/troubleshooting#your-account-managers)** and we'll provide the per-machine deployment guide. Hitting installation problems? See [Common deployment issues & troubleshooting](/enterprise/admins/troubleshooting). # Troubleshooting & Support Source: https://docs.meetjamie.ai/enterprise/admins/troubleshooting Fix the most common deployment issues, then find resources, support, and your Account Managers. ## Common deployment issues Across enterprise rollouts, a handful of issues come up again and again. Getting ahead of these prevents most failed deployments. ### App blocker prevents installation If your organization uses an application blocker (such as AppLocker), it can stop the Jamie MSI from installing or updating. This is one of the most common blockers we see. To resolve it, your admins should: * Allow **MSI files to be executed** under your app blocker policy. * Whitelist **either** of the following: * the temporary Windows folder where MSIs are downloaded for execution, **or** * MSI installers signed by **Wespond UG** (Jamie's publisher). ### Proxies must be configured system-wide If you route traffic through a proxy, make sure it's configured **system-wide on Windows**, not just at the user level. A user-level-only proxy is a frequent misconfiguration that stops Jamie from connecting at all. ### Firewall ports If Jamie installs but then fails to work, a firewall is the usual cause. Make sure the ports Jamie needs are open. Reference sheet of the endpoints and ports Jamie needs open. ### Sending an installation log to support If you hit an issue during deployment, generate an installation log and include it when you contact support — it gives the team something concrete to debug. Run the installer with logging enabled: ```powershell theme={null} msiexec /i Jamie_5.7.3_x64_en-US.msi /l*v install.log ``` Adjust the version in the filename (`5.7.3`) to match the MSI you actually downloaded. This produces an `install.log` file in the current folder. Send it to [support@meetjamie.ai](mailto:support@meetjamie.ai). ## Resources Step-by-step guides and answers to common questions. Build on top of Jamie with the API, webhooks, and MCP. ## Support * **[Help Center](https://docs.meetjamie.ai)** — step-by-step guides and answers to common questions * **Support** — reach the team directly at [support@meetjamie.ai](mailto:support@meetjamie.ai) ## Your Account Managers You can always reach out to your Account Manager, who will help you with any questions: * **Tom** — Account Manager — [tom@meetjamie.ai](mailto:tom@meetjamie.ai) * **Pleurat** — Account Manager — [pleurat@meetjamie.ai](mailto:pleurat@meetjamie.ai) # Welcome to Jamie! Source: https://docs.meetjamie.ai/enterprise/admins/welcome Your control center for rolling out and managing Jamie across your organization. ## About Jamie Jamie is your personal AI note taker. It turns any meeting — online, hybrid, or in person — into structured notes, transcripts, and action items, in 100+ languages. What makes Jamie different: * **No bot needed.** Jamie doesn't join your call as a visible participant. It captures audio in the background, so nothing interrupts your meeting. * **Works everywhere.** Online, hybrid, or fully in-person meetings — Jamie takes notes on any platform. * **Privacy-first.** Hosted in Europe, GDPR-compliant, data encrypted in transit and at rest, data residency in the EU (processing and hosting). Meeting audio is deleted after transcription, and your data is never used to train AI models. What you get from every meeting: * **Meeting notes** — clear, human-like summaries. * **Transcript** — full transcription of the conversation. * **Action items** — tasks and decisions captured automatically. It also fits your workflow, syncing notes to tools like Salesforce, HubSpot, OneNote, and Notion. ## Explore the admin guide SSO, calendar permissions, activating users, adoption, recording notice, and installation. Connect your stack and build with the API, webhooks, and MCP. Data handling, EU processing, and documents for your review. ## Need a hand? Reach out any time at [support@meetjamie.ai](mailto:support@meetjamie.ai), or see [Troubleshooting & Support](/enterprise/admins/troubleshooting) for your Account Managers. # Audit Logs Source: https://docs.meetjamie.ai/enterprise/audit-logs An immutable record of administrative and security activity in your workspace Audit logs give workspace admins a complete, immutable record of who did what in your workspace — logins, invites, role changes, meeting deletions, API key activity, and more. Use them to investigate security incidents, satisfy compliance requirements, and monitor sensitive activity. Audit logs are available on the **Enterprise** plan. ## Viewing your audit log 1. Go to **Settings → Audit Log** in Jamie 2. Browse events, most recent first Each entry records **when** it happened, the **actor** (the user, API key, or system that performed the action), the **action**, the affected **target**, and the **source** it came from (dashboard, API, mobile, desktop, webhook, or system). Sign-in context such as IP address and user agent is captured where available. ## Exporting Click **Export CSV** on the Audit Log page to export your workspace's full history. We generate the file and email you a download link, which is valid for one hour. ## Events we capture The tables below list every action recorded in the audit log. Each event also carries additional context (metadata) specific to that action — for example which field changed or which role was assigned. That metadata is available in the log entry and CSV export, but is intentionally not documented here as it evolves over time. ### Authentication | Event | Description | | --------------------------------- | ----------------------------------- | | `auth.login_succeeded` | A user signed in successfully | | `auth.login_failed` | A sign-in attempt failed | | `auth.login_throttled` | A sign-in attempt was rate-limited | | `auth.logout` | A user signed out | | `auth.session_invalidated` | A user's session was invalidated | | `auth.verification_code_consumed` | An email verification code was used | ### Workspace | Event | Description | | -------------------------------------- | ---------------------------------------------------------- | | `workspace.created` | A workspace was created | | `workspace.name_changed` | The workspace name was changed | | `workspace.logo_changed` | The workspace logo was changed | | `workspace.settings.updated` | A workspace setting was updated | | `workspace.ip_allowlist_changed` | The workspace IP allowlist was changed | | `workspace.integration_access_changed` | The workspace integration access configuration was changed | | `workspace.deleted` | A workspace was deleted | ### Members & invitations | Event | Description | | --------------------------------------- | ------------------------------------------------- | | `workspace.member.invited` | A member was invited to the workspace | | `workspace.member.invite_revoked` | A pending invitation was revoked | | `workspace.member.invite_accepted` | An invitation was accepted | | `workspace.member.invite_declined` | An invitation was declined | | `workspace.member.removed` | A member was removed from the workspace | | `workspace.member.role_changed` | A member's workspace role was changed | | `workspace.member.joined_via_domain` | A user joined automatically via a verified domain | | `workspace.invitation_link.created` | An invitation link was created | | `workspace.invitation_link.invalidated` | An invitation link was invalidated | ### Domains, SSO & directory sync | Event | Description | | ----------------------------------------- | ---------------------------------------------------------- | | `workspace.verified_domain.added` | A verified domain was added | | `workspace.verified_domain.removed` | A verified domain was removed | | `workspace.sso.portal_opened` | The SSO admin portal was opened | | `workspace.sso.enforced` | SSO enforcement was enabled | | `workspace.sso.cert_renewal_required` | An SSO certificate is approaching expiry and needs renewal | | `workspace.sso.cert_renewed` | An SSO certificate was renewed | | `workspace.scim.enabled` | Directory Sync (SCIM) was enabled | | `workspace.scim.disabled` | Directory Sync (SCIM) was disabled | | `workspace.member.provisioned_via_scim` | A member was provisioned via SCIM | | `workspace.member.deprovisioned_via_scim` | A member was deprovisioned via SCIM | ### User account | Event | Description | | ---------------------- | ---------------------------- | | `user.profile_updated` | A user updated their profile | | `user.account_deleted` | A user account was deleted | ### Billing | Event | Description | | -------------------------- | ----------------------------- | | `billing.checkout_started` | A checkout flow was started | | `billing.portal_opened` | The billing portal was opened | ### API keys | Event | Description | | --------------------- | -------------------------- | | `api_key.created` | An API key was created | | `api_key.updated` | An API key was updated | | `api_key.revoked` | An API key was revoked | | `api_key.regenerated` | An API key was regenerated | ### Webhooks | Event | Description | | ------------------------------------- | --------------------------------------------------- | | `webhook.endpoint.created` | A webhook endpoint was created | | `webhook.endpoint.updated` | A webhook endpoint was updated | | `webhook.endpoint.deleted` | A webhook endpoint was deleted | | `webhook.endpoint.secret_regenerated` | A webhook endpoint's signing secret was regenerated | | `webhook.endpoint.tested` | A test event was sent to a webhook endpoint | ### Meetings | Event | Description | | ----------------------------------- | ----------------------------------------------------- | | `meeting.deleted` | A meeting was deleted | | `meeting.processed` | A meeting finished processing | | `meeting.share.granted` | A meeting was shared with someone | | `meeting.share.revoked` | A meeting share was revoked | | `meeting.share.email_sent` | A meeting share email was sent | | `meeting.share.workspace_shared` | A meeting's workspace sharing was changed | | `meeting.share.public_link_toggled` | A meeting's public share link was enabled or disabled | ### Labels | Event | Description | | ------------------------- | ----------------------------------- | | `label.created` | A label was created | | `label.updated` | A label was updated | | `label.deleted` | A label was deleted | | `label.shares_updated` | A label's sharing was updated | | `label.meetings_tagged` | Meetings were tagged with a label | | `label.meetings_untagged` | Meetings were untagged from a label | ### Integrations | Event | Description | | ------------------------------ | -------------------------------------- | | `integration.connected` | An integration was connected | | `integration.disconnected` | An integration was disconnected | | `integration.settings_updated` | An integration's settings were updated | ### Calendar | Event | Description | | ------------------------- | ---------------------------------------- | | `calendar.connected` | A calendar was connected | | `calendar.disconnected` | A calendar was disconnected | | `calendar.states_changed` | Calendar recording settings were changed | ### Templates & AI | Event | Description | | ------------------------------------ | ------------------------------------------- | | `template.created` | A template was created | | `template.updated` | A template was updated | | `template.deleted` | A template was deleted | | `template.workspace_sharing_changed` | A template's workspace sharing was changed | | `template.auto_apply_changed` | A template's auto-apply setting was changed | | `agent.conversation_deleted` | An AI conversation was deleted | ### Audit log exports | Event | Description | | ---------------------------- | ------------------------------------------ | | `audit_log.export_requested` | An audit log export was requested | | `audit_log.export_completed` | An audit log export completed successfully | | `audit_log.export_failed` | An audit log export failed | ## Questions? If you have questions about audit logs or need help with an export, reach out to your account manager. # Directory Sync (SCIM) Source: https://docs.meetjamie.ai/enterprise/directory-sync Automatically provision users and assign workspace roles from your identity provider Directory Sync (SCIM) lets you manage your Jamie workspace membership directly from your identity provider (IdP). When it's enabled, Jamie automatically creates, updates, and deprovisions accounts as people join, change, or leave in your directory — and can assign each person the right **workspace role**. Directory Sync is available on the **Enterprise** plan and is compatible with Microsoft Entra ID, Google Workspace, Okta, and any SCIM 2.0-compliant directory. ## Set up Directory Sync 1. Go to **Settings → Security** in Jamie 2. Click **Configure** next to **Directory Connection Settings** 3. Select your directory provider and follow the on-screen instructions to connect it The setup wizard provides the endpoint URL and bearer token to enter in your IdP, with step-by-step instructions for each supported provider. If you also use SSO, set up [Single sign-on](/enterprise/sso) and domain verification first — they share the same **Settings → Security** page. ## How provisioning works Once Directory Sync is connected, your IdP becomes the source of truth for who belongs to the workspace: * **New users** in your directory are automatically added to the Jamie workspace (and a Jamie account is created if they don't have one). * **Updates** to a user's name or status sync automatically. * **Removing or deactivating** a user in your directory deprovisions them from the workspace and signs them out. While Directory Sync is active, you manage membership from your IdP — manual invites in Jamie are disabled to keep the two in sync. ## Assigning roles Every provisioned user is a **Member** by default. To grant someone the **Admin** role, configure role assignment in your directory. There are two ways to do this. ### Option 1 — Map a group to a role (recommended) Works for Microsoft Entra ID, Google Workspace, and other SCIM directories. 1. Create (or choose) a group in your IdP for your Jamie admins — for example, **Jamie Admins**. 2. During Directory Sync setup, in the role-assignment step, map that group to the **Admin** role. 3. Add your administrators to the group. Anyone in the mapped group is provisioned and kept in sync as an **Admin**. Remove someone from the group and they return to **Member** on the next sync. ### Option 2 — Use the `jamie_role` attribute If you'd rather drive the role from a user attribute than a group, map the **`jamie_role`** attribute during setup and set it on each user. | `jamie_role` value | Resulting role | | ------------------- | -------------- | | `admin` | Admin | | `member` (or unset) | Member | The value is case-insensitive. If both a group mapping and `jamie_role` are present, **Admin** from either source wins. Your IdP's own administrator status is **not** used to assign Jamie roles. For example, a Google Workspace "Super Admin" is **not** automatically a Jamie admin — Jamie only applies the group or `jamie_role` mapping you configure. If you configure neither, everyone is provisioned as a **Member**. ## Role safeguards * **Member by default.** An unrecognized or missing role always falls back to **Member**, so admin access is never granted by accident. * **Roles stay in sync.** Changing a user's group membership or `jamie_role` promotes or demotes them automatically on the next sync. * **The last admin is protected.** Directory Sync will never remove the last remaining admin from a workspace, so a misconfiguration can't lock your team out. ## Questions? If you run into any issues during setup, reach out to your account manager. # IP allowlisting Source: https://docs.meetjamie.ai/enterprise/ip-allowlisting Restrict access to your Jamie workspace to approved IP ranges IP allowlisting restricts access to your workspace to the IP ranges you define. Requests from any other address are rejected, whether they come from the web app, the desktop app, or the mobile app. IP allowlisting is available on the **Enterprise** plan and is configured by workspace admins under **Settings → Security**. ## How it works Once at least one range is saved, Jamie checks the client IP address of every request against the list. * **Sign-in.** Members of your workspace can only sign in from an allowed address. This applies to every sign-in method: email code, Google, Microsoft, Apple, and SSO. A blocked attempt shows *Login blocked: your IP address is not allowed.* * **Signed-in sessions.** Every request from the web, desktop, and mobile apps is checked, including meeting recording and Ask AI. Requests from outside the allowlist are rejected with *Access denied: your IP address is not allowed.* * **Moving networks.** A member who leaves an allowed network loses access on their next request and regains it as soon as they are back on one. There is nothing to revoke or re-issue. The allowlist applies to signed-in users. Requests authenticated with an [API key](/developers/api/access-and-security) and [MCP](/integrations/mcp) connections are not restricted by the allowlist. ## Set up the allowlist Go to **Settings → Security** and scroll to **IP Allowlist**. Enter one range per line, or separate them with commas. Both IPv4 and IPv6 are supported, in CIDR notation. A single address without a prefix is treated as exactly that address. ```text theme={null} 203.0.113.0/24 198.51.100.42 2001:db8:1234::/48 ``` Click **Test**. Jamie compares the ranges against every IP address that accessed your workspace in the last 7 days and lists any that would be blocked. Fix the list until the check reports that all recent addresses are covered. Click **Save**. The allowlist takes effect immediately for every member of the workspace. Make sure your own current IP address is included before you save. An allowlist that excludes you locks everyone out, including you, and you will not be able to sign in to fix it. If that happens, contact your account manager or [support@meetjamie.ai](mailto:support@meetjamie.ai). VPNs and Zero Trust clients such as Cloudflare WARP often route traffic over IPv6. If your team uses them, include both the IPv4 and the IPv6 ranges they exit from. ## Turn off the allowlist Clear the field and click **Save**. An empty allowlist allows all IP addresses. ## Audit log Changes to the allowlist and the sign-ins it blocks are recorded in the [audit log](/enterprise/audit-logs): | Event | Description | | -------------------------------- | --------------------------------------------------------------------------- | | `workspace.ip_allowlist_changed` | The allowlist was changed. The entry records the previous and the new list. | | `auth.login_failed` | A sign-in was blocked. The entry's reason is `ip_not_allowed`. | ## Questions? If you have questions about IP allowlisting, reach out to your account manager. # Security & Compliance Source: https://docs.meetjamie.ai/enterprise/security-and-compliance How Jamie handles your data, where it's processed, and the documents for your review. * No customer data (transcripts, summaries, etc.) is ever used for AI model training. * Audio is processed as a live stream for transcription and deleted immediately afterward — only the text transcript and summary are retained, in the EU. * All meeting content (transient audio during transcription, transcripts, summaries, and action items) is processed exclusively within the EU. ## Links for admins Review and sign the DPA. Security posture, certifications, and subprocessors. # Single sign-on (SSO) Source: https://docs.meetjamie.ai/enterprise/sso Set up SAML 2.0 SSO and domain verification for your Jamie workspace Single sign-on (SSO) allows your organization to manage authentication centrally through your identity provider (IdP). Jamie supports SAML 2.0, which is compatible with Okta, Microsoft Entra ID, Google Workspace, and any other standard IdP. Setup has two steps that must be completed in order: 1. **Verify your domain** — proves your organization owns the domain 2. **Configure SAML** — connects your IdP to Jamie Both are found under **Settings → Security** in the Jamie app. SSO security settings ## Step 1: Verify your domain Domain verification must be completed before SAML can be configured. 1. Go to **Settings → Security** in Jamie 2. Click **Configure** next to **Domain Verification Settings** 3. Enter the domain you want to verify (e.g. `example.com`) 4. You'll be given a TXT record to add to your DNS: Domain verification TXT record 5. Add the TXT record in your DNS provider (Cloudflare, Route 53, Google Domains, etc.) 6. Once added, verification is automatic — it usually takes under a minute, but can take up to 48 hours if you're updating an existing TXT record You can verify multiple domains (e.g. `example.com` and `subsidiary.example.com`) by repeating this step for each one. ## Step 2: Configure SAML Once your domain is verified: 1. Go to **Settings → Security** in Jamie 2. Click **Configure** next to **SSO-Provider Connection Settings** 3. Select your identity provider and follow the on-screen instructions SSO provider selection You'll be provided with an **ACS URL** and **Entity ID** to enter in your IdP. The setup wizard includes step-by-step instructions for each supported provider. ### Supported identity providers * **Okta** * **Microsoft Entra ID** (Azure AD) * **Google Workspace** * **Auth0** * **ADP** * **Cloudflare** * **Custom SAML** — any other SAML 2.0-compliant IdP * **Custom OIDC** ## After setup Once SAML is configured, all users signing in with one of your verified domains will be required to authenticate through your IdP. Users who previously signed in with email or social login will be redirected to SSO on their next login. ## SCIM provisioning Automatically create, update, and deprovision Jamie accounts — and assign workspace roles — directly from your IdP with [Directory Sync (SCIM)](/enterprise/directory-sync). ## Questions? If you run into any issues during setup, reach out to your account manager. # Ask AI Source: https://docs.meetjamie.ai/enterprise/users/ask-ai Chat with your meetings to get instant answers, summaries, and follow-ups.