Skip to content

Changelog

Breaking changes to a released version are announced before they ship. Additive changes — a new field, a new endpoint, a new error code — can arrive at any time, so parse defensively: ignore fields you do not recognise.

Version v1 has not been released to partners yet. Everything below is the shape of the first delivery.

  • Product access now uses persisted company features and owner permissions equally for direct and partner accounts. Partner commercial offers do not revoke granted features. New tokens require the requesting app’s feature; issued tokens keep their normal TTL, with credential revocation still immediate.

  • Meeting and call ownership follows the owner’s current default company for direct and partner accounts alike; historical event markers do not fix ownership. Credential company and visibility restrictions remain in place. WhatsApp uses saved session ownership consistently; sessions without a company keep their authorized legacy resolution.

  • Meeting template names outside the current company scope now use the stable label Template; historical answers and company-scoped template references remain available. Personal and currently authorized template names are preserved.

  • WhatsApp responses and source visibility include umbler_talk and antonella alongside WhatsApp Web and RD Conversas, using the account’s enabled integration and the owner’s permissions.

  • The OAuth protected resource metadata names the resource MCP server in resource_name, and the /oauth/authorize error page and error_description texts no longer name a brand.

Tooling

  • An official MCP server ships alongside v1: @salesbud/mcp on npm, one tool per public read operation, for Claude Desktop, Claude Code and Cursor. It is a client of this API and adds no capability of its own. See MCP server.
  • MCP server 0.2.0: new list_accounts, list_teams and list_tags tools; the list tools accept the new filters; list text names every account of each record, up to three (its name, else its domain), including email conversations, which previously printed a blank account.
  • MCP server: get_api_context says whether the credential belongs to the company or to a user, and names the owner.
  • MCP server 0.3.0: it authenticates with OAuth (Bearer), so Claude and ChatGPT can connect by signing a person in. With no credential, POST /mcp answers 401 with a WWW-Authenticate challenge whose resource_metadata points to /.well-known/oauth-protected-resource. An invalid, expired or revoked token reads as a request to reconnect, and a connection awaiting administrator approval is reported as such by the tools. Fixed X-Client-Id/X-Client-Secret headers keep working. See Connect Claude or ChatGPT.
  • MCP server 0.4.0: new find_meetings and find_calls find one account’s records by name, domain, CNPJ or id; one call reads at most 30 pages in 20 seconds and hands back its own next_cursor when a ceiling stops it. A call the client cancels stops the reads it started, and every date parameter declares format: "date-time". The text of the existing tools’ answers is unchanged. See MCP server.
  • GET /mcp/tools lists the MCP tools without a credential — the same fields tools/list returns, plus serverVersion — cacheable for five minutes. See MCP server.
  • MCP server 0.3.1: every tool declares destructiveHint: false and openWorldHint: false alongside readOnlyHint: true, so assistants know the tools only read the connected Salesbud workspace.
  • Salesbud is listed in Claude’s connector directory: Claude users connect it from the directory, with the custom connector as a fallback. See Connect Claude or ChatGPT.

Resources

  • Calls are their own collection at /v1/calls, with call_ identifiers. A record captured from a VoIP integration has object: "call"; everything else is a meeting. Ids do not resolve across collections.
  • All five routes are mirrored for both collections: list, retrieve, transcript, template answers and overall evaluation.
  • A call does not carry bot_history at all: a VoIP capture has no recording bot, so the field is omitted rather than returned empty, and a schema that validates a call rejects it. enablement.meeting_type stays and is often null — that null is legitimate, unlike the bot.
  • WhatsApp conversations of the company’s sellers are a collection at /v1/whatsapp, with wa_ identifiers and wamsg_ messages, behind whatsapp.read (metadata) and whatsapp.content.read (sanitized text, media metadata, audio transcriptions). last_message_at moves only on a new message, so re-read messages to observe edits and deletions; deleted messages stay as tombstones. Date filters compare in whole seconds. See WhatsApp.
  • Added GET /v1/accounts, GET /v1/teams and GET /v1/tags: paginated catalogs of the company’s own accounts (searchable by name or domain with q, 2 to 100 characters), teams and tags, with the opaque ids the list filters accept. Accounts without a usable domain and tags with an empty name are not listed.
  • Account references in meetings, calls, emails and WhatsApp conversations now carry name (null when the account has no name yet).

Errors

  • The error envelope is { type, title, detail, code, request_id }. Every field is snake_case; the HTTP status stays in the status line and is not repeated in the body. Branch on code.
  • POST /oauth/token keeps the OAuth 2.0 error shape required by RFC 6749 §5.2.
  • A malformed account_id, team_id or tag_id, a score_min or score_max that is not an integer from 0 to 10, score_min greater than score_max, or a q outside 2 to 100 characters is INVALID_FILTER. A well-formed id the company does not own returns an empty page, not an error. Reusing a cursor with a different filter is INVALID_CURSOR.
  • POST /oauth/token answers invalid_client with the error_description “The credential has no active owner.” for a user credential whose owner is no longer an active member of the company. User credentials add no other error code: a resource outside the owner’s view is 404.
  • New error 403 API_CLIENT_PENDING_APPROVAL (“The API client is waiting for administrator approval.”): the token belongs to a connection that is still awaiting administrator approval.
  • The existing 403 API_ACCESS_DISABLED (the company has no API access) is now documented on POST /oauth/token. It uses the error envelope above, not the OAuth error format.

Pagination and filters

  • Cursors are signed and ordered by ascending id; they bind the filters of the request that produced them.
  • updated_after, updated_before and snapshot_at were removed. Without a dedicated visibility timestamp there is no honest incremental sync; re-query by period instead. updated_at remains in responses as informative only.
  • The date filters are meeting_after and meeting_before. Earlier drafts also accepted meeting_at_from and meeting_at_to; those never reached a partner and were removed rather than shipped as deprecated on day one.
  • Added account_id and team_id to /v1/meetings, /v1/calls, /v1/emails and /v1/whatsapp, and tag_id, score_min and score_max to /v1/meetings and /v1/calls. See Filtering by account, team, tag and score.
  • team_id means the sellers whose default team it is: a seller in several teams counts for their default team only, so a seller’s meetings, calls and WhatsApp chats land in at most one team. Sellers with no team, and RD Conversas chats, belong to no team, so summing the teams can fall short of the unfiltered total. The assignment is the current one, not the team on the date of the record. A team with no members returns an empty page. On emails it matches a conversation when any of its threads is in a team member’s mailbox, so a conversation shared across teams can appear under more than one. On WhatsApp it covers only numbers a team member connected directly; RD Conversas chats have no owner and are excluded when team_id is set.
  • score_min and score_max are inclusive and count completed evaluations only: a meeting being re-evaluated drops out of a score-filtered list until the new evaluation completes.
  • Cursors issued before this change stay valid: the new filters enter the cursor only when present.

Access

  • The API is gated by the API_ACCESS company feature, checked at token issuance and at credential creation.
  • Emails and account domains are normalised to lower case in every field, so an address can be used as a join key regardless of where it appears.
  • The catalogs are open to any collection read scope: meetings.read, calls.read, emails.read or whatsapp.read.
  • Added user credentials: requested by any member of a company with API_ACCESS who does not approve credentials, approved by an administrator, 3 active and 1 pending per user in each company, valid for 30, 90, 180 or 365 days counted from the approval, and limited to what their owner can see in Salesbud, including meetings shared with them. Company credentials are unchanged. See Two kinds of credential, Requesting a user credential and User credentials see what their owner sees.
  • Rotating a user credential, which only its owner can do, stops the previous secret at once and sets a new validity.
  • GET /v1/context now returns client.kind (company or user) and client.owner, the owner of a user credential (null for a company credential).
  • A user credential’s token carries only the scopes its owner can use at issuance time, and changes to the owner’s teams, shares or permissions reach the API within 60 seconds.
  • With the ACCOUNTS feature off, a user credential gets an empty /v1/accounts, accounts: [] on meetings, calls, email and WhatsApp conversations, and an empty page when filtering by account_id.
  • Added OAuth sign-in for connectors: GET /oauth/authorize, the authorization_code and refresh_token grants of POST /oauth/token (the token response carries a rotating refresh_token for them, and the OAuth error invalid_grant is new), and discovery metadata at /.well-known/oauth-authorization-server and /.well-known/oauth-protected-resource. The connection is a user credential that reads what the person who signed in can see, waits for administrator approval when that person does not approve credentials, and is listed on the API credentials screen with the app’s name. client_credentials is unchanged. See Connect Claude or ChatGPT.
  • Salesbud‘s own apps, such as the AI collaborators, connect through the same OAuth sign-in under the AI collaborators feature instead of API access, and POST /oauth/token answers 403 AI_COLLABORATORS_DISABLED without it. Their connection is active without approval and stays out of the per-user limits. See Requesting a user credential.
  • Added POST /oauth/revoke (RFC 7009): a refresh token or an access token of a connection made by signing in revokes the whole connection, and the answer is 200 with an empty body even for an unknown token. The authorization server metadata announces it in revocation_endpoint. See /oauth/revoke.
  • Renewing a connection an administrator disabled answers invalid_grant with "error_description": "The connection was disabled by a company administrator."; it renews again once it is enabled.
  • POST /oauth/revoke and the owner’s revoke no longer turn a connection an administrator disabled into a revoked one: its refresh tokens end and it stays disabled until an administrator revokes it.