Menú

Recursos

Registro de cambios

La API se versiona por fecha. Cada organización queda fijada en la versión con la que creó su primera API key; un pedido puede pedir otra viva con MB-Version. Un cambio que rompe sale siempre en una versión nueva, y la anterior sigue viva 12 meses.

Los cambios se publican en inglés.

2026-11-01

VigentePublicada el 2026-11-01
  • First public version of the MB Suite API: the CRM, the platform (keys, webhooks, trash, me, catalog), the organization's accounts, and every module that gets its operations registered during the rollout.
  • snake_case everywhere; cursor pagination with limit, cursor, has_more and next_cursor; one error format with a stable code; Idempotency-Key on POST, PATCH and DELETE; irreversible and bulk operations need dry_run + confirm_token except the simple DELETE of a restorable record.
  • Before its release (2026-10-15), this version changed for the early integrations in the ways listed next; they are part of 2026-11-01, not of a new version.
  • Writes that send or change something outside MB Suite (an email, a WhatsApp message or SMS, a post or a reply on a network, a change in Meta or in another platform where its terms allow it through the API, a domain), that have a separate cost (AI, DataForSEO, a crawl), or that leave a send scheduled or switched on (a scheduled campaign or report, an activated flow, an enrollment into an active sequence) now need dry_run + confirm_token through the API and the MCP, like bulk and irreversible operations (x-mb-confirm: always). Without the token the call is 409 confirmation_required, with details.external (the platform) and details.effect (sends, sends_later or pays).
  • A few of them need it only with certain data (x-mb-confirm: conditional, the condition in x-mb-confirm-when; needs_confirmation_when in the MCP), and without that data they run directly: seo_rankings.keywords.add and seo_rankings.keywords.import when fetch_search_volume is true; members.invitations.create when send_email is true; crm.deals.update and crm.prospects.update when the owner changes to an AI position, and crm.prospects.create when it is created with one; crm.conversions.config.set_enabled and social_inbox.positions.set_enabled when they switch it on; social_inbox.rules.set_enabled when it switches on a rule that replies by itself; social_inbox.positions.create and social_inbox.rules.create when the position, or a rule that replies, is created on; crm.ai_team.positions.create when the position is created on (payload.enabled true: created off, the default, it attends no one and runs directly); social_inbox.positions.update and social_inbox.rules.update when the change turns on a position that was off or switches a Community Manager that is on to auto_reply, or leaves answering by itself a rule that was not; deliveries.deliveries.create when it is created with a schedule.
  • dry_run: true works on any write, through the REST API too (it was 400 dry_run_not_supported outside bulk, irreversible and confirmable operations): it writes nothing, returns the operation's own preview or a generic one, and its needs_confirmation says whether running it needs the confirm_token. A read has no dry run: 400 dry_run_not_supported, also through the MCP.
  • Inputs are strict: a field the operation does not declare, at any depth, is 400 unknown_field (param names the first one and details.unknown_fields all of them) instead of being silently dropped, so a misspelled filter never returns an unfiltered list and an update never answers ok without changing anything. Maps (custom fields, headers, ids used as keys) and open objects still take any key, except scope of the Social Inbox positions, which takes only its sources; the contract and the MCP schemas mark the strict objects with additionalProperties: false. A cursor that is not the next_cursor of the same list is 400 invalid_cursor.
  • In most operations the output carries only what its schema declares: undeclared fields (the organization repeated inside each record such as organization_id, storage paths, ordering and normalization helpers) no longer come out. Internal fields (search indexes, denormalized access lists, locks) never come out, in any operation.
  • Renamed fields: media_planner.templates.list and get give a system template's place as order (it was position), like the organization's own templates; organizations.ai_usage.get gives agents[].configured_model (it was model); monitors.cross_account.get groups its metrics as platforms[].metrics (it was sections[].metrics); media_planner.cross_account.overview gives accounts[].account_id and account_name (they were org_id and org_name); alerts.history.list gives and filters by alert_id (it was alert_config_id).
  • WhatsApp is one word in field names: reminder_whatsapp and on_whatsapp_reply in inputs (the old reminder_whats_app and on_whats_app_reply are still accepted), and confirmation_whatsapp_sent_at, dnd_whatsapp, whatsapp_sound and last_seen_whatsapp_at in outputs.
  • Lists that now return a summary per row, with the whole record in their get: crm.reengagement.list and web_forms.forms.list (without each form's fields and auto_responder).
  • crm.ai_team.states.list and crm.ai_team.states.get give goal_progress.fields_captured as a map by the position's field keys (it was a list).
  • Defaults that changed: fetch_search_volume of seo_rankings.keywords.add and seo_rankings.keywords.import is false (fetching it is paid); a lead source created through the API has ai_qualification.enabled false unless it is sent (the AI scoring is paid); web_forms.forms.create leaves the form as a draft (enabled false) unless it is sent; and some lists return a smaller page when limit is left out: 20 in crm.deals.list, crm.persons.list, crm.prospects.list, crm.my_leads.list and crm.lists.members.list; 25 in activity.entries.list, crm.conversations.list, crm.calls.list, crm.sequences.enrollments.list, social_inbox.items.list and task_management.tasks.list; 50 in crm.lead_sources.list.
  • Outputs that can come as null: a media plan's total_budget and total_spent (also in its webhook events) are null when the plan has amounts in more than one currency: read budget_by_currency. Output schemas also declare as nullable the fields that can come empty.
  • A deletion that keeps a copy returns trash_item_id (and trash_item_ids when it leaves more than one) with trash_expires_at; platform.trash.restore takes that id, or entity_type + entity_id.
  • Through the MCP, idempotency_key (next to id in mb_execute_operation, or as an argument of each typed write tool) does what the Idempotency-Key header does in the REST API: the same key with the same call returns the stored result instead of running it again.
  • crm.lead_sources.list no longer returns the webhook secret of each source: crm.lead_sources.get returns it, one source at a time, to whoever can manage sources (never through the MCP).
  • Behavior that changed: crm.assignment_rules.delete with an id that does not exist is 404 (it answered ok); dashboards.inherited_explorer_views.remove of a view that is no longer inherited returns removed: false; child_orgs_count leaves out deleted Accounts; task_management.packs.apply no longer counts subtasks in tasks_created; deliveries.deliveries.update merges email_config with the saved one field by field (null clears a field) instead of replacing it whole. A send that starts the contact with a CRM person marked «Do not contact» or unsubscribed, which used to go out, is now refused before anything is sent with 409 do_not_contact (details.recipients and details.overridable): crm.email.send (the person found by any email in to or cc), crm.whatsapp.send_template and crm_phones.sms.send (found by the number). Only a person of the team can confirm it, with do_not_contact_override on a closed basis (transactional: about their account, a charge or support; requested_by_person: they asked us to write) and, outside the app, a reason (400 do_not_contact_reason_required without it); an AI agent's API key cannot (403 do_not_contact_override_not_allowed), and a marketing WhatsApp template never goes to them, confirmed or not. A reply to what the person wrote after being marked still goes out, and the response says so in do_not_contact. crm.exports.create writes a cell that a spreadsheet would read as a formula (one that starts with =, +, - or @, numbers and phones aside) with a leading ', and fills a daterange custom field with its from and to; a cell that already starts with apostrophes before a formula character gets one more ('=x is written ''=x), and crm.imports.create removes that one apostrophe (a cell '@user imports as @user; it imported '@user). crm.exports.create takes headers for deals too (it was 400; prospects still reject it): with labels the header row has the deal table's column names in your language, status and rotting come in your language and checkbox custom fields as yes/no. In its deal CSVs, owner is the member's current name or, without one, the name stored on the deal (it fell back to the user id, which stays in the ownerId column), user, member and members custom fields come by name (they came as user ids), and probability is the one the forecast uses: the stage's unless it was set by hand (closed deals keep theirs). Persons and companies with headers: labels name the email, phone, last-modified, unsubscribe and bounce columns Email, Phone, Last modified, Unsubscribed date and Bounced date (they were Primary email, Primary phone, Updated, Unsubscribed and Bounced). crm.deals.update and crm.deals.move keep a won or lost deal's probability when it changes stage or gets probability_overridden: false (it took the stage's).
  • The AI teams of the CRM and the Social Inbox: crm.ai_team.positions.create and crm.ai_team.positions.update reject an ai_agent_id that is not a saved agent of the organization (a draft neither) nor an MB Suite agent that exists and is not hidden (400 ai_agent_not_found), or whose channel is not whatsapp_crm (400 ai_agent_not_eligible); they saved it. social_inbox.positions.create and social_inbox.positions.update give 400 ai_agent_not_eligible for a Community Manager agent whose channel is not social, and reject a source in scope that does not exist (400 invalid_input; it was dropped silently). They all check the agent only when it changes. ai_agent_not_found comes as 400 invalid_request when a write names an agent that is not there, and as 404 when reading or deleting one. crm.ai_team.positions.create is 409 limit_reached past 25 AI positions per organization, as duplicating already was. With include_brain, crm.ai_team.positions.get resolves the brain as the position's AI does on each turn: an MB Suite agent chosen as its own and, without a usable one, the organization's default agent for its type, with chosen_in (position, areas or type_default); for the same data, the agent and configured_agent_missing can differ from before.
  • crm.attribution.web_tracking counts in deals, won (and total_deals, total_won and conversion_rate) the deals generated by the leads of each group, the WhatsApp conversations and Web Forms submissions matched in the period: in the group of their lead, once each (the first lead of the period gets it), a prospect when converted, a merged deal as the deal it was merged into; archived deals and the earlier deal a returning contact lands in are left out. It counted the deals created in the period from a WhatsApp click, grouped by the deal's own attribution, so form leads came with 0 deals and some groups came with deals and no leads (those groups are gone). A Web Forms click referred by the organization's own site groups as direct|direct|, and match_score_avg leaves out Web Forms submissions, which carry no score.
  • x-mb-bulk gives the bulk limit as max_items; maxItems still comes and is deprecated.
  • AI credits: the AI that an organization runs charges its AI credits everywhere. Re-indexing the knowledge of an agent after ai.agents.update, ai.agents.update_live or ai.agents.create_with_key charges the caller's credits; without them, the entry keeps the error code (ai_credits_exhausted, ai_personal_limit or payment_required) and the agent keeps what was already indexed. social_brand_voice.profile.update can return ai_summary_status no_credits (a save from the app whose person had no AI credits left: the summary was not rewritten), with the reason in the new ai_summary_credits_code (one of those three codes). The AI blocks of a report instance carry ai_error_code when their AI did not run for lack of AI credits. profile.documents.*, social_brand_voice.documents.* and the social_brand_voice.document.processed event can carry those three codes in error_message or embedding_error (read but not indexed: status ready with embedding_status error). The scheduled runs of report deliveries do not write AI blocks: the PDF they send is built from the template, so the AI text never reached it (ai_mode is kept for later). Every model answer is charged by its real tokens (thinking as output, what the model read with its web tools as input, the model that answered), also an unusable one; a model that fails is not charged. The organization's automatic AI checks its credits first: without them, an AI position of the CRM hands the lead to its escalation person (crm.ai_team.positions.assign and assigning an AI position as owner with crm.deals.update or crm.prospects.update return the owner after that first turn) and a lead scored on arrival enters unscored, marked for the daily refresh (crm.leads.ingest). A completed Series post can come without copy, with the reason in the new copies_error (ai_credits_exhausted, ai_personal_limit, payment_required or generation_failed).
  • Rank tracking keeps every position with its own engine, country and device: seo_rankings.projects.list and seo_rankings.projects.get add measured_pos_keys, combo_summaries (one per combination measured today that already has measurements, each with its own previous_summary) and retired_combos, and seo_rankings.projects.create and seo_rankings.projects.update return the project with them; previous_summary is always the check before of the same combination as check_summary, or null, and summaries add last_checked_at. seo_rankings.keywords.list and seo_rankings.keywords.get mark each latest position with measured: what the project no longer measures is cleared, and a leftover may come with measured: false (the cleanup removes it): skip it (its history stays in seo_rankings.history.grid). seo_rankings.projects.update that removes a country or a device clears its latest positions and returns positions_cleared and retired_pos_keys, and its dry_run says which combinations it would stop measuring and how many positions it would clear (running it needs no confirm_token). seo_rankings.keywords.bulk.set_engines, and seo_rankings.keywords.add when it reactivates a keyword with engine_keys, clear the latest position of the countries a keyword leaves (the dry run of seo_rankings.keywords.bulk.set_engines counts them in positions_cleared). seo_rankings.keywords.opportunities rejects a position_key the project does not measure (400 rank_position_key_not_measured, with details.measured_pos_keys) and counts only the keywords measured there. seo_rankings.keywords.import rejects a device the project does not measure (400 rank_import_device_not_in_project). Each row of seo_rankings.cross_account.get names its combination and adds combos, and avg_top10_pct uses each project's main combination. In demo organizations seo_rankings.checks.trigger and seo_rankings.volume.fetch are 403 demo_read_only, also on their dry run, and seo_rankings.keywords.add and seo_rankings.keywords.import never fetch search volume. Webhooks: the seo_rankings.check.completed event adds previous_summary and combo_summaries, the seo_rankings.project.updated event adds retired_pos_keys and positions_cleared, and the summaries of the events add last_checked_at.
  • The Social Inbox and Idempotency-Key (2026-10-17): the Social Inbox event item.escalated adds the reason ai_no_answer (in a private message, the AI came back empty twice, also on its retry, and the item goes to a person instead of staying unanswered), and its description says the AI Community Manager did not answer. A write whose response could not be stored (too large, or the save failed, also a response that cannot be written as text) answers a repeat with 409 idempotency_response_unavailable instead of running it again.
  • Routines for every Account (2026-10-18): ai.work.routines.create and ai.work.routines.update take scope.mode: all_accounts (without org_ids; only with a template whose all_accounts is true in ai.work.routines.templates, which adds that field): the workspace and every Account of it the owner reaches with the template's modules, resolved again at each run, where the AI works only in the Accounts with something to report. A routine of every Account comes back with scope.mode all_accounts, empty org_ids and names, and scope.total (how many Accounts it covered the last time); its preview has no items. New 400 codes: ai_work_scope_all_accounts_template and ai_work_scope_no_accounts; ai_work_routine_trigger_accounts now also covers all_accounts, and org_ids with all_accounts is 400 ai_work_scope_invalid. ai_work_template_unknown adds details.allowed (the template keys that exist), and the preview of a routine with a template in this organization or in chosen Accounts may warn that there is nothing to review there.
  • LinkedIn documents and carousels (2026-10-20): social_planner posts take format: document (LinkedIn page and profile only: one PDF, PowerPoint or Word file of the media library, up to 100 MB and 300 pages) and linkedin_settings.document_title (up to 100 characters; null removes it); linkedin_profile now takes carousel and document too. On LinkedIn a carousel now goes out as a swipeable document that MB Suite builds from its 2 to 20 images, with a title (a feed post with several images still goes out as a grid): scheduling a LinkedIn carousel with fewer than 2 images, or with no text and no title, is post_not_ready with the new issues linkedin_carousel_too_few_images and linkedin_document_title_missing, and a document post adds linkedin_document_invalid, linkedin_document_needs_format, linkedin_document_too_large, linkedin_document_too_many_pages and linkedin_document_encrypted. A post's master_media and a publish job's content_snapshot can carry type: document (or media_type: document) with page_count and document_flags, and the snapshot of a LinkedIn document or carousel job, document_title. media_library.items.*, media_library.uploads.create (purpose: document_thumbnail) and the media_library.item.* events add type: document, page_count and document_flags; a document whose name lacks the extension of its type gets it. The sync of published posts no longer rewrites the format of a post created in MB Suite, and posts brought from LinkedIn come as feed or document. Through an API key or the MCP, scheduling or publishing on LinkedIn answers 403 platform_terms_restricted (its terms do not allow automated publishing); drafts work.
  • API & MCP per workspace (2026-10-20): an owner or admin of the workspace turns the API and the MCP on in Settings → API & MCP, and they apply to all its Accounts; they are included in active paid subscriptions, not in the free trial. A call to an organization whose workspace has not turned them on, or turned them off, is 403 platform_not_enabled; one where the subscription that applies is on its free trial or there is none is 403 platform_requires_paid_plan, with details.plan (trial or no_plan). platform_disabled now only means that MB Suite has not opened them for the organization or the person. Turning them off deletes nothing: API keys, connections and webhooks work again when they are turned back on, and meanwhile no webhook events are generated. organizations.modules.list gives blocked_reason own_switch for API & MCP and organizations.modules.toggle_free answers it with 409 module_has_own_switch: it has its own switch.
  • Email sending hold (2026-10-20): MB Suite pauses an organization's email sending when a campaign send or a flow has a hard bounce rate over 5% (with at least 50 sent and 10 hard bounces), or when MB Suite decides it. While it is paused, email_marketing.campaigns.send_now (and its dry run), email_marketing.flows.activate and email_marketing.templates.send_test are 409 email_sending_paused (details.kind bounce_rate or manual, details.held_at); a scheduled campaign does not go out and goes back to draft with schedule_missed.code email_sending_paused; active flows send nothing; and the campaigns that were going out are cancelled with send_held (held_at, kind, sent, total). Campaign stats add hard_bounced; email_marketing.usage.get adds sending_paused (paused, kind, since); and the dry runs of email_marketing.flows.enrollments.enroll, email_marketing.flows.enrollments.enroll_audience and email_marketing.flows.audiences.link say so and count sending_paused.
  • AI tiers and extra credits (2026-10-20): credit packs are retired; an organization adds a monthly AI tier (plus, pro or max) and prepaid extra credits, used once the month's credits run out, up to a monthly cap set by the owner (both are bought and configured only in the app). organizations.ai_usage.get adds balance.base_plan_credits, balance.tier and balance.extra_credits (null when balance.paid_by is workspace: they belong to whoever pays); balance.plan_credits_included is the plan plus the tier; balance.total_remaining adds the extra credits usable now; balance.pack_credits_remaining is deprecated and carries those usable extra credits. ai_credits_exhausted adds details.detail: extras_limit (the owner's monthly cap for extra credits was reached) or frozen (the extra credits are frozen).
  • Platforms with their own terms (2026-10-20): the terms of Google Ads, Google Business Profile, LinkedIn, TikTok and YouTube limit what can be done with their data and their APIs outside MB Suite's screens, and the API, the MCP and webhooks follow them (the permissions guide lists what is available for each one). A call they do not allow is 403 platform_terms_restricted, with details.platform, details.network and details.access (none, or own_key: only an organization API key, through the REST API), and the MCP does not offer those operations. Where a response mixes platforms, their records are left out (a deal or prospect that came in through LinkedIn Lead Gen or TikTok, a person or company created or completed from them, a linked account, a connection, an Inbox item from TikTok or a LinkedIn comment) or come without their fields (the campaign names of a CRM record's attribution, the answers of a Google Ads lead form, a media plan's total_spent and spent_by_currency). Webhooks do not send the events that carry their data (a lead from their forms, a change in their campaigns, an alert on their metrics, a delivery whose PDFs map their accounts, the media plan cost sync) and send the rest without their fields; an event type already subscribed stays in the subscription. social_inbox.crm.create_prospect is 409 for a LinkedIn comment or mention (also in the app). New fields: origin_channels in CRM people and companies, source_channel and origin_channels in the results of crm.search, and platforms in deliveries.received.list and in the delivery events.