The base URL will not change. It is
https://api.meetingkit.com/api/v1 and it is going to
stay that way. There is no /api/v2, and no migration waiting for you.Versioning
A new version is minted only when we change something in a way an existing integration could notice — the left column of the table below. Everything else ships to every version the day it lands, so a pinned integration still gets new endpoints, new fields, and new options without doing anything. Your pin. Every API key is pinned to the version that was current the first time it called the API, and stays there until you move it. Requests without a header are served at your pin. The header. SendHappyscribe-Version: 2026-09-15 on any request to be served as the
API was on that date. Any date works — it resolves to the newest version on or before it —
so you can pin your code to the day you wrote it. This is per request and never changes
your key’s pin.
The response tells you where you are. Every response carries the version that served
it, and, when you are behind, a link to the next breaking change above your version:
Breaking entries name, then move your pin —
contact support with your account email until the setting reaches your account page.
Webhook endpoints carry their own pin, set when the endpoint is created.
What you must tolerate
Your integration has to keep working when any of these happen. We ship them without warning, and they are not breaking changes:New properties appear in responses
New properties appear in responses
Parse defensively. Do not assume a response object contains only the keys you know about, and do
not fail validation on unrecognized ones.
Properties change order
Properties change order
JSON objects are unordered. Never depend on key position.
New values appear in existing enums
New values appear in existing enums
This is the one that breaks integrations most often.
state, failure_reason, export formats,
and language codes all gain values over time. Handle unknown values as a default case — never
write an exhaustive switch that throws on anything unfamiliar.New optional request parameters
New optional request parameters
Existing calls keep their current behavior when you omit them.
New endpoints and resources
New endpoints and resources
Additive by construction.
New webhook event types
New webhook event types
Your receiver must ignore event types it does not recognize, and respond
2xx rather than
erroring.Signed URLs change format, length, or expiry
Signed URLs change format, length, or expiry
audioUrl, videoUrl, downloadUrl, and upload URLs are short-lived and signed. Fetch them
fresh each time. Never cache them, parse them, or persist them.Opaque strings change format
Opaque strings change format
Object IDs and error messages are opaque. Their length and format can change, including gaining
or losing fixed prefixes. Store IDs as strings with room to spare.
What we promise
Everything else. We will not remove an endpoint, remove a response field, rename either, make an optional parameter required, or change what an existing call does, without going through the deprecation process below.Deprecation and removal
Removals are rare, and they never arrive unannounced. When we deprecate something, three things happen at once. 1. The response tells you. Deprecated endpoints return an RFC 9745Deprecation header and a link
back to this page:
Deprecated entry naming the replacement.
3. If and when a removal date is set, the endpoint additionally returns an
RFC 8594 Sunset header with that date:
Sunset header has no removal date. That is the normal
state — we deprecate long before we remove, and most deprecated paths simply stay
alive.
Before removing anything we check whether it is still being called. If it is, we
extend the window and contact the organizations still using it directly.
A deprecated path or field is eventually removed at the current version by a new
breaking version. It keeps working, unchanged, for every key pinned before that version.
Deprecated paths
These still work and will keep working. They are not in the API reference because they are no longer the recommended way to call the product.
The
/transcriptions rows are a pure rename: each path keeps its exact behavior and
response shape under /conversations. GET /orders/:id and POST /orders/:id/confirm
are not deprecated — Orders remains the resource you retrieve and confirm.
Release history
2026-10-12
snake_case everywhere Breaking Every response field is snake_case: workspaces, people, companies, conversations and their meeting, orders, upload instructions, memberships, folders and translation tasks rename their camelCase fields (createdAt is created_at, audioUrl is audio_url), and their timestamps, with glossaries’ and style guides’, are in UTC (Z). Every version still accepts both spellings as input.
-
Changed on workspaces:
created_at,updated_at,owner_email,members_count,is_human_transcription_allowed,is_human_translation_allowed,email_domains,idp_company_names. -
Changed on people, wherever they appear (
/people, bots,bot.*webhooks, conversations):full_name,primary_email,job_title,linkedin_url,photo_url,avatar_url,enriched_at,created_at,updated_at. -
Changed on companies, wherever they appear:
primary_domain,employee_count,founded_year,short_description,website_url,linkedin_url,logo_url,total_funding,annual_revenue,latest_funding_stage,created_at,updated_at. -
Changed on conversations:
created_at,updated_at,delivery_estimated_at,sharing_enabled,share_code,public_url,audio_length_in_seconds,failure_reason,failure_message,soundwave_url,audio_url,video_url,cost_in_cents,_links.self.download_url,_links.self.editor_url; on theirmeeting:start_time,end_time,ical_uid,calendar_event_id,calendar_id,calendar_owner_role,summary_url. -
Changed on orders:
can_be_submitted,outputs_ids. OnGET /uploads/new:signed_url. On memberships:organization_id,organization_name,created_at,updated_at. On folders:parent_id. On translation tasks:failure_reason,progress_percent,target_language,translated_transcription_id. -
Changed the timestamps of those objects, and
created_atandupdated_aton glossaries and style guides, to ISO 8601 in UTC (2026-09-29T09:00:00.123Z), keeping their milliseconds; an order’stranscriptions[].estimated_atis2026-09-29T09:00:00Z. They were sent with the server’s offset (2026-09-29T11:00:00.123+02:00).
bot.* event recorded before this release and resent: see
The bot records a meeting below.
Metadata, flat and merged Breaking
A bot’s metadata is a flat map of strings, like every other object’s: up to 50 keys of up to 40 characters, values up to 500 characters. PATCH /bots/{id} merges it instead of replacing it ("" removes a key, metadata: "" removes them all), and a value stored as a nested object or a number reads as its JSON text.
- Changed
metadataon bots,POST /botsandPATCH /bots/{id}: nested objects, arrays andnullare refused with a422naming the key; numbers and booleans are stored as their text; keys keep the casing you send. - Added
metadatato members, workspaces (for owners and admins), brand kits and webhook endpoints, andPATCH /meetings/{id}to set a meeting’s. These are additive and reach every version. A change of a meeting’s metadata alone sends nomeeting.updatedwebhook.
PATCH /bots/{id}, returned as stored.
Connect links create members lazily Breaking
Calendar connect links no longer take or return email_match: a link for a member_id connects whichever account the member approves. member_data.email pre-fills the provider’s sign-in instead.
- Removed
email_matchfromPOST /calendar_connect_linksand the link object. Sending it is a422. - Added
member_data(name,email,metadata) toPOST /calendar_connect_links: the member is created when the user consents. Not withmember_id. Additive, on every version.member_data.nametakes the same names asPOST /members(up to 100 characters, not a URL): a longer one is a422when you create the link instead of aconnect_failedafter consent. - Added
member_idto the query string of the return toreturn_url, on every version.
email_match (default exact) and read links without
member_data.
Policies keep what a partner can use Breaking
Policies and a meeting’s policy and effective_policy drop what a MeetingKit partner cannot use: transcription.model, transcription.glossary_ids, access, summary_email, storage, recording.methods.bot.silence_detection.automatic_leave and recording.methods.bot.join_at_scheduled_start; writes naming them are refused.
- Removed from workspace, member and meeting policies (
GETandPATCH /policies/{id}) and from a meeting’spolicyandeffective_policy, on meetings,POST /calendar_eventsandmeeting.*webhooks:transcription.model,transcription.glossary_ids,access,summary_email,storage,recording.methods.bot.silence_detection.automatic_leaveandrecording.methods.bot.join_at_scheduled_start. Bots keep joining 3 minutes before the scheduled start. - Changed a
PATCH /policies/{id}naming one of them to answer422withunknown_field. - Changed
recording.methods.bot.startto read onlyon_joinoron_command. A start set in the MeetingKit app readson_join.
policy: read a member’s policy with GET /policies/{member id}.
Calendar connections without a method Breaking
Calendar connections no longer carry method, which was always user_consent.
Brand kits in groups Breaking
Brand kits are two groups, bot (name, tile, chat) and email, where each choice is a type and an object named after it. They replace bot_name, background_color, logo_scale, logo_url, custom_thumbnail_url, send_chat_message, chat_message, chat_message_paused, brand_color, brand_url, brand_description and apply_branding_to_summary_emails, and a write naming a field the kit does not have is refused.
- Changed
bot.tilechooses the notetaker’s tile:typeisdefault,color,logoorimage, with the options of the chosen type in the object of the same name. The other types keep their options, so switching back restores them. - Changed
emailchooses the summary email:typeisdefaultorbranded. A kit created through the API starts ondefault. - Added
email.branded.cta, the button of a branded email:open_meeting(the button it always had),none, orlinkwith alabeland aurlwhere{{meeting_id}}is the meeting’s id. - Added images from files: send a
file_id asbot.tile.logo.file,bot.tile.image.fileoremail.branded.logoand read{ id, url }back. A write takes that object too, so thebotandemailof a read can be sent back as they are. The email logo is the tile’s logo until it gets one of its own. - Added
workspace_idon brand kits: the workspace that owns the kit,nullwhen the key does not belong to it. Keys pinned before 2026-10-09 read it as an integer. - Changed
POST /brand_kitstakes the workspace that owns the kit asworkspace_id, which may be left out when the key belongs to exactly one workspace, and no longer needsworkspace_ids: a kit can exist with no workspace using it.PATCH /brand_kits/{id}may emptyworkspace_ids. - Changed
PATCHwrites only what it names, at any depth;422withunknown_fieldnames a field the kit does not have, onPOST /brand_kitsandPATCH /brand_kits/{id}. - Changed a brand kit is no longer removed when the last workspace stops using it. It stays,
visible to the keys of the workspace that owns it and editable by that workspace’s owners and
admins, until
DELETE /brand_kits/{id}. - Removed
GETandPATCH /workspaces/{id}/brand_kit. UseGET /brand_kits?workspace_id=andPATCH /brand_kits/{id}.
meeting.updated at every version; from
2026-10-12 its data.object can read the same as the previous event.
One calendar event object Breaking
POST /calendar_events returns the calendar event object instead of the meeting, and meeting.calendar_event is that same object (cev_ id, meeting_id, times, people, metadata) instead of { ical_uid, series_id }. series_id is gone: recurring says whether the event repeats.
- Changed
POST /calendar_eventsreturns the calendar event object: one per meeting, the same for every member who has the event, with themeeting_idof its meeting. - Changed
meeting.calendar_eventis that same object, and on everymeeting.*webhook. - Added
metadataonPOST /calendar_events, kept on the calendar event and merged on every push. Additive: accepted at every version. - Removed
calendar_event.series_id, which was alwaysnull.
POST /calendar_events and { ical_uid, series_id } in meeting.calendar_event.
Meeting platforms meet, zoom, teams Breaking
A meeting’s platform is meet, zoom or teams (Microsoft Teams Live included) instead of google_meet, zoom, microsoft_teams or microsoft_teams_live.
Any other platform stays null. Keys and webhook endpoints pinned before 2026-10-12 keep the old names.
After this release, the next change to a meeting may send one more meeting.updated webhook, with
a new event id, whose data.object is unchanged at versions before 2026-10-12. A resend of an
event from before this release carries the calendar event object at 2026-10-12, built from what
the event recorded: no metadata or response, and recurring: null.
The bot records a meeting Breaking
A bot is one way of recording a meeting: it carries meeting_id and flat fields (bot_name, welcome_message, recording_start, recording_mode, silence_alert, summary_language, summary_template) instead of settings, results are read only from GET /meetings/{id}/results, and conversation_id, transcript, speakers, summary, summary_sections, action_items, people and companies are gone from the bot.
- Added
meeting_idto the bot and everybot.*webhook.POST /botscreates the meeting at once, so it is set fromqueued. - Changed
POST /bots: sendbot_name,bot_avatar,welcome_message,recording_start(on_joinoron_command),recording_mode,silence_alert,summary_languageandsummary_templateat the top level. A request withsettingsis refused with400.glossaries,destination_folder_id,access_level,send_recording_toandsilence_detection_automatic_leaveare no longer taken. - Changed the recording choices to be the meeting’s: each one sent becomes the meeting’s
own policy, each one left out follows the member’s and the workspace’s, and
PATCH /policies/{meeting id}can changetranscription.language,summary.language,summary.templateandrecording.methods.bot.silence_detection.alerton a meeting started by a bot until it begins. - Removed
settings,conversation_id,transcript,speakers,summary,summary_sections,action_items,peopleandcompaniesfrom the bot andbot.*webhooks. Read the results withGET /meetings/{meeting_id}/results. - Added on every version:
member_idonPOST /bots(the meeting is that member’s), andinclude=wordsonGET /meetings/{id}/resultsfor each segment’s word timings.
settings and reading the bot
with its results exactly as before, and a webhook retry or resend carries the body the event had
when it happened.
Files
- Added
POST /filesandGET /files/{id}, on every version: upload an image (PNG, JPEG, GIF, WebP or SVG, up to 10 MB) to a workspace asmultipart/form-dataand get a file with afile_id and a signedurl.purposesays what it is for;brand_kitis the only one for now. Keys pinned before 2026-10-09 read itsworkspace_idas an integer.
- Added
preview_urlandpreview_expires_aton the brand kit, on every version: a page that shows the kit as saved (the notetaker’s tile in the call, its chat messages and the summary email). Anyone with the link can open it without signing in. The link lasts ten minutes, and every response carries a fresh one.
- Added
client_reference_idon the member, onPOST /membersandPATCH /members/{id}, on every version: your own id for the person, stored and returned, never interpreted.nulluntil you set it. - Changed
POST /workspaces: the new workspace has every feature on, nothing is billed, and its speakers are identified from its first meeting. Nothing to configure.
2026-10-10
Results show results only Breaking Meeting results no longer readpending: a part is none until a recording produces it. Whether one will come is the meeting’s phase, recordings and effective_policy.recording.
- Removed
pendingfromresults.statusand from each part’s status (transcript,summary,media) on meetings,GET /meetings/{id}/resultsandmeeting.*webhooks. A meeting whose bot is queued, joining or in the call readsnoneuntil the recording is in, thenprocessing,readyorfailedas before. - Added to the
Meetingobject reference which field gives each label (will be recorded, recording, processing, not recorded).
pending while a bot is
queued, joining or in the call and has no transcript yet. A webhook retry or resend carries
the value the event had when it happened. After this release a meeting with a bot in one of
those states sends one more meeting.updated webhook, with a new event id, whose
data.object is unchanged at your version.
2026-10-09
One error body Breaking Every error,401 and 429 included, is { "errors": [{ "code", "message", "field" }] }: code is a stable word, field is the dotted path to the request field at fault and is present only then, and the body no longer repeats the HTTP status.
- Changed every endpoint’s error body to the
errorsarray, with acodeon every error:missing_api_key,invalid_api_key,rate_limited,not_found,internal_errorand the rest listed on Errors. - Changed
fieldto the full dotted path (settings.language) where some endpoints sent only the last segment, and droppedfield: "base"from workspace errors. - Fixed
429responses to carry the documentedretry_in_seconds, on every version. - Added
messageto the workspace, order and validation errors that were anerrorsarray without one, on every version.
Happyscribe-Version: 2026-10-09 to get the new 401. The
previous bodies are:
- A single
errorstring, for most endpoints:{ "error": "Not found" }. A missing or invalid key also repeats the status:{ "error": "Invalid API key.", "status": 401 }. - Bots:
{ "errors": ["Meeting url must be a valid URL"] }, and on409also a top-levelcode(bot_not_editable,bot_not_cancellable). - Workspaces:
{ "errors": [{ "code": "workspace_update_not_allowed", "field": "base", "message": "…" }] }. - Members, meetings, policies, brand kits and calendar connections already used the
errorsarray for the errors they raise themselves. What changes on them is what the API raises before or around them: authentication (401), rate limits (429), a malformed body or missing parameter (400), an invalidHappyscribe-Version, a generic409and500.
wks_…, gls_…, tpl_…, cns_…) instead of integers, everywhere they appear. Every version still accepts the integer ids as input.
- Changed workspace ids to
wks_…: the workspaceid, everyworkspace_idon meetings, members, calendar connections, calendar connect links and agent sessions,workspace_idson brand kits, a workspace policy’sowner.id,organizationIdon organization memberships, and the/workspaces/{id}and/policies/{id}paths. - Changed glossary ids to
gls_…: the glossaryid,glossary_idsin policies and in orderoperations, and the bot’sglossaries. - Changed custom summary template ids to
tpl_…in a policy’ssummary.templateand the bot’ssummary_template. Built-in templates keep their slug, such assales_call. - Changed consent event ids to
cns_…, including the/consent_events/{id}path.
_links.next) and error messages that name a workspace carry wks_ ids on every
version; follow links as they are. After this release each meeting sends one more
meeting.updated webhook, with a new event id, whose data.object is unchanged at your
version. Folder, person and company ids are unchanged.
2026-10-08
Recording scopemeetings:organized Breaking
The recording scope meetings:hosted is now meetings:organized (meetings the member organizes). Both names are accepted when you write a policy.
- Changed
policy.recording.scopeon members and inGET/PATCH /policies/{id}readsmeetings:organized. Versions before 2026-10-08 keep readingmeetings:hosted.
2026-10-01
Calendar connections Breaking Members no longer carrycalendar_connection, which was always null: read a member’s calendar with GET /calendar_connections?member_id=.
- Added
POST /calendar_connect_linksandGET /calendar_connect_links/{id}: a hosted link that takes one of your users through Google or Microsoft consent and connects their calendar, creating the member from the connected account when you don’t name one. - Added
GET /calendar_connections,GET /calendar_connections/{id}andDELETE /calendar_connections/{id}: your members’ connected calendars, with their status and last sync, and a disconnect that keeps past meetings. - Added the
calendar_connection.connected,calendar_connection.disconnectedandcalendar_connection.errorwebhooks. - Removed
calendar_connectionfrom members.
calendar_connection: null on members. The new
endpoints and webhooks reach every version.
- Changed a workspace created with
POST /workspacesto start with its meeting language detected (auto) rather than English. On every version.
- Added
GET /settings,POST /settings,GET /settings/{id},PATCH /settings/{id},DELETE /settings/{id},GET /settings_assignments,POST /settings_assignmentsandDELETE /settings_assignments/{id}. Settings are values; an assignment applies them where every condition of itsscopeholds (workspace:,member:,meeting:,meetings:organized|internal|domain|external). See Settings. - Added
settingson workspaces (andGET /workspaces/{id}), members and meetings, and inmeeting.*webhooks: the values that apply,sourcesnaming the assignment that supplied each one (or areason), and every assignment that reaches the object. Lists add it withinclude=settings. - Removed
GET /policies/{id}andPATCH /policies/{id}, andpolicyandeffective_policyon meetings and inmeeting.*webhooks. - Changed a meeting’s own choice to reach that occurrence only, and
nullto a code:inheriton any field,same_as_transcriptforsummary.language,noneforbranding.brand_kit_id.
- Changed
GET /workspacesandGET /consent_eventsto the list every other endpoint returns:{ "object": "list", "data", "has_more", "_links" }, paged withlimit(default 25, at most 100) and the_links.next.urlcursor.workspacesandresultsare nowdata. - Removed
pageandper_page: every list in this reference answers400to either.
404 not_found in the error body, before the API key is checked. A known path called with a method it doesn’t take answers 405 method_not_allowed with an Allow header. Both used to return an HTML page.
- Changed bot errors on
meeting_url: a link we can’t join, a Microsoft Teams organization that blocks outside bots, no notetaker free and an unexpected failure each say what happened. The code is stillinvalid. - Changed every
401(no key, an unknown key, a key that may not do this, an invalid agent handoff code) to theerrorsbody on every version; it no longer depends onHappyscribe-Version. - Changed
POST /membersandPOST /calendar_eventsrefuse an email on a disposable-mail or reserved test domain (such asexample.com) with a message naming the domain. The code is stillinvalid.
api.meetingkit.com
The base URL is https://api.meetingkit.com/api/v1, with an API key from MeetingKit. The docs
live at https://docs.meetingkit.com.
2026-09-30
Policies Breaking A member carries itspolicy (what it sets: recording scope, bot, transcription, summary, access and summary email) instead of settings, and policies change through PATCH /policies/{id}.
A meeting carries its policy (what was set on it) and effective_policy (what will happen, with recording.enabled_because) instead of settings and recording.reason.
- Added
GET /policies/{id}andPATCH /policies/{id}: what gets recorded and how, set on a workspace, a member or a meeting. - Changed members:
settingsis nowpolicy, in the policy shape (recording.scopeinstead of the fourauto_jointoggles,accessas grants). - Changed meetings:
settingsis nowpolicypluseffective_policy, andrecording.reasoniseffective_policy.recording.enabled_because.
settings on members and
meetings (and meeting.* payloads), with the values that apply.
2026-09-29
Meetings API Breaking Bot ids start withbot_ instead of mtg_ (the characters after the prefix are unchanged and /bots/{id} accepts both), mtg_ now identifies a Meeting, consent events carry their bot as bot_id instead of meeting_id, and the meeting.* webhooks are sent.
- Added
GET /meetings,GET /meetings/{id}andGET /meetings/{id}/results: every meeting of a workspace, whether it came from a calendar or a bot, with its transcript as speaker turns, its speakers, summary and media once it is over. - Added the
meeting.created,meeting.updatedandmeeting.finishedwebhooks. - Changed bot ids:
mtg_01k6b0…is nowbot_01k6b0…. Store either;/bots/{id}andGET /consent_events?bot_id=accept both. - Changed consent events:
meeting_idis nowbot_id, and themeeting_idfilter onGET /consent_eventsis nowbot_id.
mtg_ bot ids and
meeting_id on consent events, and receive no meeting.* webhooks. GET /meetings/{id}
with a bot’s mtg_ id keeps returning that bot.
Calendar connections
- Changed calendar connections are available to every workspace, on every version:
POST /calendar_connect_linksno longer returns403 feature_not_enabled.
- Added
POST,GET,PATCHandDELETE /webhook_endpoints: register, list, change and remove a workspace’s webhook receivers. The create response is the only one with the signingsecret, and a new endpoint is pinned to the API version of that request. - Added
POST /webhook_endpoints/{id}/test: sends a signed sample and returns your receiver’s answer. - Added
GET /webhook_eventsandGET /webhook_events/{id}: the events sent to your endpoints for 90 days, with every delivery attempt and its response, andPOST /webhook_events/{id}/redeliverto send one again.
- Fixed meeting, results, bot and consent event timestamps, and the
created_atof every webhook event, to be ISO 8601 in UTC (2026-09-29T09:00:00Z) as documented. They were sent with the server’s offset (2026-09-29T11:00:00+02:00): the same instant, a different string. Every version gets it. People and companies embedded in a bot (enrichedAt,createdAt,updatedAt) still carry the server’s offset for now, so one bot payload can mix both forms: parse timestamps as instants rather than comparing strings. The next time a meeting’s webhooks are computed, it can send onemeeting.updated, with a new eventid, that differs only in its times. Events sent before keep their original timestamps if resent.
2026-09-28
Pick a brand kit on the workspace policy- Added
branding.brand_kit_idon the workspace policy (GET/PATCH /policies/{workspace_id}): the brand kit the workspace uses, ornullfor MeetingKit’s default branding. It is the same link as a kit’sworkspace_ids. Meetings show the kit their bot is sent with ineffective_policy.branding.
- Added
POST /calendar_events: push a calendar event as Google Calendar, Microsoft Graph or Nylas returned it (or in a small shape of your own) and get the meeting for that occurrence (source: calendar_sync). Every version gets it. - Added
no_meeting_urltoeffective_policy.recording.enabled_because: a calendar meeting with no link to join now says recording is off. Keys pinned before 2026-09-30 readsettings.recording.reason: unsupported_platformfor it.
2026-09-26
Silence alert and silence leave Breaking The bot settingsilence_detection is now silence_detection_alert, and the new silence_detection_automatic_leave controls whether the bot leaves after 20 minutes of silence.
- Changed
settings.silence_detectionis renamedsettings.silence_detection_alert. It keeps its meaning: an audible and chat alert when nobody speaks, and the bot stays.settings.silence_detectionis now a400. - Added
settings.silence_detection_automatic_leave, defaulttrue. Every bot has always left after 20 minutes of continuous silence; sendfalseto keep it in the call.
settings.silence_detection, and
their bots keep leaving after 20 minutes of silence.
Brand kits you can share
A brand kit is now a resource with its own id, and one kit can brand many workspaces.
- Added
POST /brand_kits,GET /brand_kits,GET /brand_kits/:id,PATCH /brand_kits/:idandDELETE /brand_kits/:id. A kit’sworkspace_idslists the workspaces that use it; set it on create or update to brand many workspaces at once. - Deprecated
GETandPATCH /workspaces/:id/brand_kit. They keep working, on the kit the workspace uses.
2026-09-25
Meeting bots are bots Breaking The meeting bot is now abot: its object type is bot, provider_reason is failure_message, its webhooks are bot.* instead of meeting.*, and transcription_id, summary_markdown, consent and settings.language are gone.
- Changed the canonical paths are
POST /botsandGET /bots/:id./meeting_botsand/meetingskeep working on every version and return aDeprecationheader. - Changed the bot’s
objectisbot(wasmeeting), andprovider_reasonis renamedfailure_message. - Changed the six bot webhooks are
bot.recording_started,bot.recording_completed,bot.transcript_ready,bot.summary_ready,bot.failedandbot.cancelled(weremeeting.*). An endpoint pinned before this version keeps receiving themeeting.*names and the previous payload, and a subscription made with an old name still matches. - Removed
transcription_id(useconversation_id),summary_markdown(usesummary_sections) andconsentfrom the bot.consentis removed on every version; it was never enabled for an API workspace. - Removed
settings.language. Send the top-levellanguage;settings.languageis now a400. - Added
created_atandended_aton the bot, on every version.
- Added
PATCH /bots/:idmoves a bot created through the API when its meeting moves:join_atandmeeting_urlwhile the bot isqueued,participantsuntil the call ends,metadatauntil the bot is finished. The bot keeps its id. - Added
DELETE /bots/:idcancels a bot created through the API. Aqueuedbot becomescancelledand sendsbot.cancelled; a bot in the call leaves and keeps what it recorded; a bot with a recording answers409.
meeting.cancelled.
Members
- Added
POST /members,GET /members,GET /members/{id},PATCH /members/{id}andDELETE /members/{id}: a partner’s end users inside a workspace. Create is idempotent onworkspace_idandemailand sends no email; the person never hears from MeetingKit. Members carrymem_ids, which the MeetingKit resources reference asmember_id. A member’ssettings(the four auto-join toggles, recording, summary and access defaults) are read-only for now. Every error these endpoints raise themselves (400, 404, 409, 422) is anerrorsarray ofcode,messageand, on 422, a dottedfield(settings.recording.mode).
2026-09-18
Agent sessions- Added
POST /agent_sessionsexchanges the code a user copies from the agent handoff page for their personal API key and a workspace id. No API key is needed for the call. It is the endpoint auth.md tells AI agents to use.
- Added
summary_markdown,summary_sectionsandaction_itemson the meeting resource, onmeeting.summary_readywebhooks and onGET /conversations/:id/summary, next to the unchanged plain-textsummary. Sections keep the summary template’s headings and bullets; an action item carries anassigneeonly when it opens with the name of exactly one meeting participant or linked Person. - Added
speakerson the meeting resource, onmeeting.transcript_readywebhooks and onGET /conversations/:id: every distincttranscript[].speakerlabel with its talk time and, when Voice ID resolved it, the Person’s name, email and id. - Fixed
people[]on a meeting and on a conversation only lists People of the workspace you are reading through. A conversation moved between workspaces could list People linked by the workspace it left.
2026-09-16
Brand kit updates- Fixed
PATCH /workspaces/:id/brand_kitreturns422namingapply_branding_to_summary_emailswhen it is sent asnull. Previously the request failed with500. - Added the brand kit reference now lists what the endpoint already accepts:
nullforbot_nameandsend_chat_message, and up to 2048 characters forbrand_url.
2026-09-02
Version pinning- Added the
Happyscribe-Versionrequest header. Any ISO date resolves to the newest version on or before it, for that request only. - Added every response carries
Happyscribe-Version, the version that served it, and aLink: …; rel="successor-version"to the next breaking change when you are behind. - Added API keys are pinned to the version current at their first request, and webhook endpoints carry their own pin. See Versioning.
- Changed the
Authorizationheader accepts the bare API key.Bearer <key>andToken <key>still work, and the scheme is now case-insensitive. The docs show the bare form everywhere. - Changed a JSON request body is parsed as JSON even when its
Content-Typeis missing,text/plain, orapplication/x-www-form-urlencoded. Multipart is untouched. - Added specific
400messages for a body that is the string[object Object](afetchcall withoutJSON.stringify) and for malformed JSON, which now names the parser’s position. - Changed a request with no
Authorizationheader returns401withMissing API key. Send it as "Authorization: <your key>"., and a key that matches no account returnsInvalid API key.— previously both saidUnauthorized. Every v1 endpoint now answers an unauthenticated request with401; a few returned500. - Changed
POST /meeting_botsandGET /consent_eventsreturn400namingworkspace_idwhen it is blank. Previously they returned401, which read as a bad key. - Fixed a trailing slash on an API URL no longer redirects. The redirect turned a
POSTinto aGETin most HTTP clients, silently listing instead of creating.
2026-09-01
Deleting a busy conversation says so Breaking- Changed
DELETE /conversations/:id(and its legacy/transcriptionspath) returns409with a message naming the state when the conversation is in a pipeline stage that cannot be cancelled —aligning,retrying,unlocked,automatic_transcribing_second_part, andpro_transcribing/human_translatingonce the human work has started. It previously returned401, which read as an authentication problem. A genuine permission denial still returns401. - Fixed a successful delete is documented as
204, which is what it has always returned.
- Added every webhook to the OpenAPI spec and the API reference sidebar,
collocated with its resource’s endpoints: the six
meeting.*events under Meeting Bots,consent.grantedandconsent.revokedunder Consent, and the orderwebhook_urlcallback (order.state_changed) under Orders. No wire format changed — these webhooks already existed; they are now part of the documented, versioned API surface.
2026-08-31
Transcriptions are now conversations The product treats every recording — an upload, a meeting, a dictation — as a conversation, and the API now does too.- Added
GET /conversations,GET /conversations/:id,PATCH /conversations/:id,DELETE /conversations/:id,GET /conversations/:id/summary, andGET /conversations/:id/convert_to_subtitles— same behavior and response shapes as their/transcriptionscounterparts. Write requests use aconversationbody wrapper. - Added
conversation_idsonPOST /exportsandsource_conversation_idonPOST /translateare accepted wherevertranscription_idsandsource_transcription_idwere. When both are sent, the legacy name wins. - Added Export responses include
conversation_idsand meeting objects (in API responses and webhook payloads) includeconversation_id, alongside their unchanged legacytranscription_ids/transcription_idkeys. - Deprecated the read-and-manage
/transcriptionspaths — use/conversations. They keep working unchanged, andtranscription-wrapped request bodies remain accepted everywhere as a legacy alias. See deprecated paths.
transcription-named keys, and the
transcription/subtitles operation values are unchanged.
2026-08-21
Meeting participants carry emails again- Fixed
meeting.participantson transcription responses returned{ "email": null }for everyone but the organizer. It was rendering the in-room display names the recording provider reports at the end of a call, not the invite list — so no attendee could be mapped to a person. - Changed
meeting.participantsandmeeting.organizernow always carry anamekey,nullwhen nothing exposed one. It was previously omitted. - Changed
meeting.participantsis now identical to theparticipantsarray on the meeting resource and on meeting webhooks. Meeting rooms and the notetaker itself are excluded from all three. - Changed
nameis now resolved from every source that could know it — the calendar invite, whatever you supplied at creation, the names people displayed in the call, and the people your workspace has already met — keeping the fullest. Expect a name where you previously sawnull, and a fuller one (“Marc Assens”) where you previously saw a partial one (“Marc”).
2026-08-14
Consent events Consent collected by the Trust Engine is now visible in the API.- Added
GET /consent_eventsandGET /consent_events/{id}— high-level consent outcomes (notified,accepted,declined,revoked,deletion_requested) per participant and meeting. - Added a
consentblock on the meeting object —status(pending/partial/complete/objected) plus each participant’s latest outcome. - Added webhook events
consent.grantedandconsent.revoked, carrying the same consent event object.
- Added
GET /workspaces/:id/brand_kitreturns the workspace’s active brand kit configuration: notetaker bot name, video tile avatar colors and scaling, logo URL, in-call chat greeting messages, and summary email white-labeling settings. - Added
PATCH /workspaces/:id/brand_kitupdates workspace brand kit settings (owners and admins; other members get403with codeworkspace_update_not_allowed, and plans without notetaker customization get403with codeplan_upgrade_required). Supports partial updates.
- Added
POST /workspacescreates a workspace with the caller as its owner.403with codeworkspace_creation_not_allowedwhen workspace creation is restricted for the caller; creation is rate limited per caller. - Added
PATCH /workspaces/:idrenames a workspace (owners and admins; other members get403with codeworkspace_update_not_allowed). - Added
DELETE /workspaces/:idarchives a workspace (owner only; others get403with codeworkspace_archive_not_allowed). Archiving cancels the workspace’s subscription, removes all member access, and permanently deletes its content after 30 days — see the endpoint reference before using it. - Changed
GET /workspacesis paginated: 100 per page by default (per_pageup to 500), oldest first, with a_links.nextURL. It previously returned at most 500 workspaces with no way to page further. - Changed Missing-parameter errors name
workspace_ideverywhere except the Organization memberships endpoints, which keep their documentedorganization_idparameter and wording.
- Added
GET /workspaces— same behavior and fields asGET /organizations, under aworkspacesresponse key. - Added
workspace_idis accepted whereverorganization_idwas, including inside resource-wrapped bodies liketranscription: { ... }. When both are sent,organization_idwins. - Deprecated
GET /organizations— useGET /workspaces. It keeps working, with an unchanged response shape, andorganization_idparameters remain accepted everywhere as a legacy alias. See deprecated paths.
- Added
POST /transcribe,POST /subtitle, andPOST /translate— one creation endpoint per service, replacing the generic create-order call. Request bodies are flat (noorderwrapper), and the subtitle/transcription split is in the path — there is nois_subtitleparameter. - Changed On the new endpoints,
confirmdefaults totrue: one call starts processing. Passconfirm: falseto preview pricing first, then submit withPOST /orders/:id/confirm. (The deprecated create paths keep theirconfirm: falsedefault.) - Deprecated
POST /ordersandPOST /orders/translation. Both still work unchanged, with the same request and response shapes as before. See deprecated paths.
2026-08-10
Meeting bots- Changed
POST /meetingsis nowPOST /meeting_bots, andGET /meetings/:idis nowGET /meeting_bots/:id. The name says what the resource is: a bot you dispatch to a call, not the meeting itself. - Deprecated
POST /meetingsandGET /meetings/:id. Both still work and route to the same controller. See deprecated paths. - Added Per-request settings on meeting bot creation, so a single bot can override the organization defaults for that call.
- Fixed Creating a bot into a destination folder now requires write access on that folder. Bot avatars are restricted to JPEG.
- Fixed The settings echoed back on create now match what the bot actually does, and
the top-level
languageis validated rather than silently ignored.
2026-07-24
Delete cancels in-progress work Breaking- Breaking
DELETE /transcriptions/:idnow cancels any in-progress transcription before deleting it. Previously a delete issued while work was running could leave the job to run to completion.
2026-07-16
Meeting notetaker- Added
POST /meetingsdispatches a notetaker bot to a Zoom, Google Meet, or Microsoft Teams call. - Added
GET /meetings/:idreturns lifecycle status, and the transcript and summary once the bot has finished. - Added Signed organization-level webhooks for the meeting bot lifecycle. See Webhooks.
2026-07-06
Public sharing URLs- Added
publicUrlon transcription responses, pointing at the public share page when sharing is enabled for the file.
2026-06-23
Meeting summaries- Added
GET /transcriptions/:id/summaryreturns the generated summary as plain text. - Added
summaryUrlon transcription responses, linking to the same summary.
2026-05-21
Memory — people and companies (private beta)- Added
GET /peopleandGET /people/:id— the people discovered across your meeting transcripts, withsearch,email, andcompany_idfilters. - Added
GET /companiesandGET /companies/:id, withsearchanddomainfilters. - Added
person_id,person_email,company_id, andcompany_domainfilters onGET /transcriptions, and embeddedpeopleandcompaniesarrays on transcription responses. - Added
audioUrlandvideoUrlon transcription responses. Both are short-lived signed URLs — fetch fresh, never cache.
404 on these
endpoints. See People and companies.
2026-05-10
Pagination, folders, and meeting metadata- Added
meetingon transcription responses for files that came from a meeting — title, platform, start and end time, calendar identifiers, organizer, and participants. - Added
folder({ id, name }) on transcription responses. - Added
PATCH /transcriptions/:idacceptsfolder_id, moving the file between folders. - Fixed
GET /transcriptionsnow honorsper_page. It was previously ignored.
2026-04-17
Organizations and memberships- Added
GET /organizationslists the organizations you belong to. - Added
GET /organization_memberships,POST /organization_memberships,PATCH /organization_memberships/:id, andDELETE /organization_memberships/:idfor managing members.
2026-02-09
Glossaries and style guides- Added
GET /glossariesandGET /style_guides. - Added
glossary_idsandstyle_guide_idonPOST /orders, applying custom terminology and formatting preferences to the order.
2025-11-25
Orders become the write path- Added
POST /orderscreates transcription and subtitling work. Passservice: autoorservice: pro, andis_subtitle: truefor subtitles. - Deprecated
POST /transcriptions— usePOST /orders. - Deprecated
POST /task/transcription_translation— usePOST /orders/translation. - Deprecated
GET /task/transcription_translation/:id— useGET /orders/:id.
2025-05-08
Export download URLs- Added
downloadUrlon export responses, so a completed export can be fetched without a second call.
2025-03-11
Orders for human services- Added
GET /orders/:idreturns state, pricing, and outputs once fulfilled. - Added
POST /orders/translationplaces a translation order from a source transcription and target languages. - Added
POST /orders/:id/confirmsubmits an order created withconfirm: false, after reviewing the quoted price. - Added
tagson transcription create and on transcription responses.
2024-06-11
Vocabulary removed Breaking- Breaking The
vocabularyparameter was removed from transcription create. Custom terminology returned later as glossaries.
2023-08-08
Convert to subtitles- Added
GET /transcriptions/:id/convert_to_subtitlescreates a subtitle file from an existing transcription.
2023-02-28
Delete transcriptions- Added
DELETE /transcriptions/:id.
2021-02-08
Subtitles and folder filtering- Added Subtitle creation through the public API.
- Added
folder_idfilter onGET /transcriptions.
2020-08-12
Updates and translation- Added
PATCH /transcriptions/:idfor renaming a file and changing its sharing configuration. - Added
POST /task/transcription_translationandGET /task/transcription_translation/:idfor translating a transcript.
2020-04-09
Response slimming Breaking- Breaking
total_countwas removed fromGET /transcriptions. Computing it required a full scan on every list request. Use_links.next.urlto page instead. - Breaking Undocumented attributes were removed from transcription responses:
mediaUrl,audioLength,audioUpdatedAt,policy,lastVersionUrl,versions,publicShowUrl, andpublicEditUrl.
2019-08-07
Public and internal APIs split Breaking- Breaking
/api/v1became the public API. Endpoints that existed only to serve the MeetingKit web app — user, folders, versions, batch operations, and transcription unlock — moved to an internal namespace and are no longer reachable.
2019-02-15
Public API launch- Added
GET /transcriptions,GET /transcriptions/:id, andPOST /transcriptions. - Added
GET /uploads/newreturns a signed URL for uploading media directly to MeetingKit storage. - Added
POST /exportsandGET /exports/:idrender a transcription to a downloadable file. - Added HAL-style
_linkson responses, including_links.next.urlfor pagination.