API changelog
Every change to the public API, newest first.
Within a version, changes are additive (see versioning): new endpoints, fields, enum values and error codes can appear at any time and are listed here. Anything breaking ships as a new version or with a deprecation period announced here first.
2026-09-29: new token prefixes
Additive. New tokens carry the product's prefix; old ones keep working:
- Store API tokens now start with
mobi_st_(wasbod_). Existingbod_tokens are accepted until they are rotated or revoked; rotating one returns amobi_st_value. - Refresh tokens now start with
mobi_rt_(wasbpr_). Abpr_token still trades atPOST /v1/auth/refresh, for amobi_rt_one.
Treat tokens as opaque: don't validate them by prefix or length.
2026-09-29: ask the documentation
Additive. New tag Help:
POST /docs/ask: answers a question from the documentation, streamed as an AI SDK UI message stream (text/event-stream) with the pages it cites assource-urlparts. Public, no token; 20 questions per hour per visitor,rate_limited(429) beyond that. The docs site's Ask AI panel uses it.
2026-09-29: plans, billing and setup
Additive. Mobiari is now sold in self-serve plans; these endpoints expose the organization's subscription.
- New tag
Billing:GET /billing/plans: the plans on sale with prices (BRL cents) and limits. Public, no token.GET /tenants/{tenant_id}/subscription: plan, status (trialing,active,past_due,suspended,canceled), trial end, paid period, the open invoice and usage against the plan's limits.PUT /tenants/{tenant_id}/subscription: change plan or billing cycle (owner only;usage_exceeds_planwhen the organization would not fit).POST /tenants/{tenant_id}/subscription/cancel,.../resume,.../pay(issues or returns the invoice to pay, with a Pix "copia e cola").GET /tenants/{tenant_id}/invoices: aPageof invoices.
- New
GET /stores/{store_id}/onboarding: the store's setup checklist (products, payments, freight, brand, WhatsApp, team, custom domain, first order), read from its data. POST /tenantsaccepts an optionalplan_code(the plan to try). A new organization starts a 14-day trial and defaults toBRL,America/Sao_Pauloand the caller's language.- New error codes, status 402:
plan_limit_reached(withlimit,max,used,planindetails) fromPOST /storesandPOST /stores/{store_id}/invites;subscription_inactivefrom those and from the assistant chat when the subscription is suspended or canceled. GET /stores/{store_id}/assistant/usage:capis now the plan's monthly limit per store instead of a fixed 500.- Storefront themes: new
routes.products_url(prefix of product pages), andshop.currencyis the store's currency (BRL) so| moneyprintsR$ 19,90.
v1 (2026-10)
The API gets a version, a single error shape, a single list shape and
refreshable sessions. The unversioned paths keep answering during the
transition, but they serve the new behaviour: the changes below reach an
existing integration whether or not it moves to /v1.
Migrating
- Point your client at
https://api.mobiari.com/v1and switch every moved path to its new one. - Branch on the error
code, not on theerrortext; expect 422validation_failedwhere you handled 400. - Treat any 2xx as success: many creates now answer 201 and many actions 204 with no body.
- Read lists from the
Pageenvelope (data,next_cursor) and passinclude_total=truewhere you show a total. - Send an
Idempotency-Keyon POSTs you retry, such as orders, refunds and messages. - If you sign in with a password, keep the
refresh_tokenand callPOST /v1/auth/refreshwhen the access token expires. Store API tokens (bod_…) need no change. - Accept enum values you do not know: many string fields became enums in this version, and new values can appear without a new version.
Versioned base path
- The API lives at
https://api.mobiari.com/v1. The Storefront API is under/v1/storefront-api/.... - The unversioned paths still answer, with the new behaviour, plus
Deprecation,Link: <…>; rel="successor-version"and, once a date is set,Sunsetheaders. See Deprecation. - An unknown path under
/v1returns 404route_not_found. - Two OpenAPI documents describe the documented surface:
admin.jsonandstorefront.json. The API reference is generated from them.
Errors have a code
- Every error response is now
{"error": "<message>", "code": "<stable_code>", "details"?: {...}}. codeis new and stable: branch on it.errorkeeps the message, so clients that readerrorkeep working, but its wording may change.- Error bodies that used other shapes (
{"message": …},{"ok": false, "message": …}, plain text, bare status codes) now use this one. Any extra fields they carried moved underdetails. - Validation failures are 422
validation_failedwith per-field messages indetails.fields.
See Errors.
One list envelope: Page
- Lists return
{"data": [...], "has_more": bool, "next_cursor": string | null}, plustotalwhen you passinclude_total=true. - This replaces bare arrays and the per-endpoint envelopes such as
{"orders": [...], "total", "limit", "offset"}and{"products": [...], "total", "page"}. - Paging uses
limit(1 to 200, default 50) and an opaquecursor;offsetremains for jumping to a page, in place ofpageparameters. totalis no longer computed by default.
See Pagination.
Idempotency keys
- New: send
Idempotency-Keyon a POST to make retries safe. Replays within 24 hours return the original response withIdempotent-Replayed: true. - New error codes: 409
idempotency_request_in_progress, 422idempotency_key_reused.
See Idempotency.
Rate limits
- New: per-caller budgets per one-minute window (1200 requests/minute
authenticated, 300 anonymous by default), reported in
RateLimit-Limit,RateLimit-RemainingandRateLimit-Reset. - Over budget: 429
rate_limitedwithRetry-After.
See Rate limits.
Sessions with refresh tokens
- New:
POST /v1/auth/loginreturnstoken,refresh_token(bpr_…),expires_inandtoken_type, or a two-factor challenge, told apart bystatus. It supersedesPOST /login, whose token lasted 24 hours and could not be renewed. - New:
POST /v1/auth/refreshtrades a refresh token for a new pair. Refresh tokens rotate on every use; reusing one revokes the whole session (401refresh_token_reused). - New:
POST /v1/auth/logout,GET /v1/me/sessionsandDELETE /v1/me/sessions/{session_id}. - Store API tokens (
bod_…) are unchanged.
See Authentication.
Unchanged
- Outbound webhook payloads and signatures.
- Money in integer cents, timestamps in RFC 3339 UTC, UUID ids.
Endpoint-level changes
What changes per area, grouped like the API reference. Paths are relative to the base URL and link to their reference page.
- Moved paths are written
old → new. Where a line says "old path still answers", the old unversioned path keeps working with the new behaviour, but it has no/v1form and sends noDeprecationheader: switch to the new path. Pagemarks a list that now answers thePageenvelope; its old shape follows when it was not a bare array.- Across every area, validation failures that were 400 (often plain text)
are now 422
validation_failed. Only other changes are listed.
Auth
POST /register→POST /auth/register, answers 201. Old path still answers.POST /login→POST /auth/login. Old path still answers, with the new response.- Sign-in answers
{"status": "authenticated" | "two_factor_required", ...}and addsrefresh_token,token_type,expires_in,refresh_token_expires_inandsession_id. - Wrong e-mail or password: 401
invalid_credentials(was 400 plain text). POST /verify-totp→POST /auth/2fa/totp/verify. Old path still answers. It accepts only a sign-in challenge token (401invalid_challenge) and no longer fails with a 500 on a bad one.- Two-factor and one-time-code errors are 401 or 400 with codes such as
invalid_code,no_pending_codeandtoo_many_attempts(send code, verify code). - An access token whose session was revoked is refused. Deactivating a user revokes all their sessions.
POST /auth/refresh: retrying with the previous refresh token within 30 seconds, while its replacement is unused, re-issues a pair instead of ending the session.- A refresh token of a session ended on purpose (sign-out, device revoke,
password change) is 401
invalid_refresh_token, notrefresh_token_reused.
Me
GET /totp-setup→GET /me/2fa/totp/setup. Old path still answers.POST /enable-totp→POST /me/2fa/totp/enable. Old path still answers.- 204 with no body (was text or
{"status": ...}): enable TOTP, disable TOTP, confirm WhatsApp 2FA, disable WhatsApp 2FA, change password. - Enabling TOTP twice is 409
totp_already_enabled; a wrong current password is 400incorrect_password. PATCH /me:localeaccepts onlypt-BRoren-US; other values are rejected (were coerced).- Changing the password signs out every other session.
Tenants
GET /tenants:Page.POST /tenantsanswers 201; a duplicate name is 409slug_taken.- Security: reading and updating a tenant require the permission in that tenant, not in any store you belong to.
Stores
GET /stores:Page.POST /storesanswers 201; it checks the permission in the target tenant (403missing_permission) and a duplicate name is 409slug_taken.PATCH /stores/{store_id}/settings: invalid values are 422validation_failed(were 400).
Team
Page:GET /users, members, invites, roles, a user's roles, role audit.- Security:
GET /userslists the users of your teams only (was every user on the platform). - 204 with no body: remove member, cancel invite.
- 409 with a code: removing the owner (
cannot_remove_owner), deleting a system role (system_role), changing the owner's roles (owner_roles_locked). - Security: accepting an invite for
an e-mail that already has an account requires that account's password
(401
invalid_credentials); an account with 2FA gets no session from an invite.
API tokens
GET /stores/{store_id}/api-tokens:Page.- Security: rotating another
user's token is 403
not_token_owner(it returned that token's secret).
Webhooks
/webhook-events→GETandPOST /stores/{store_id}/webhooks/{webhook_id}/events,DELETE .../events/{event}. Old path still answers.Page: webhooks, deliveries, webhook events.POST /stores/{store_id}/webhooksanswers 201.- Security:
DELETEchecks that the webhook belongs to the store in the path.
Events
GET /stores/{store_id}/events:Page(was{events, total, limit, offset}).
Audit
GET /stores/{store_id}/audit:Pageof events (was{events, actions}). The action list moved toGET /stores/{store_id}/audit/actions.- The audit log was always empty (a query error); it now returns entries.
Places
GET /places/autocomplete:Page.
Products
/product-variants→/stores/{store_id}/products/{product_id}/variantsand/stores/{store_id}/variants/{variant_id}. Old path still answers./product-variants/{id}/optionsand/variant-options→/stores/{store_id}/variants/{variant_id}/options. Old paths still answer./option-groups→/stores/{store_id}/products/{product_id}/option-groupsand/stores/{store_id}/option-groups/{option_group_id}. Old path still answers./option-values→/stores/{store_id}/option-groups/{option_group_id}/valuesand/stores/{store_id}/option-values/{option_value_id}. Old path still answers.GET /stores/{store_id}/products:Page(was{products, total, page, limit});page,per_pageandsort_orderare gone.Page: variants, option groups (now with their values), option values, images, suggested products, slug redirects, spec suggestions (was{values}).- Product
statusis snake_case (draft,active,archived); capitalized input is still accepted. Optiongroup_typeis an enum. Webhook payloads keep their old casing. - Get, create and update return the full product; create answers 201 (was a 6-field summary). A clashing slug is made unique (was a 500).
- 201 on create for variants, option groups and option values. Assigning a variant option returns the variant with its options; setting suggestions answers 204.
- New error codes:
product_not_found,variant_not_found,sku_taken,slug_taken,invalid_color,image_not_found; 409product_has_orders,variant_has_orders,variant_has_stock,option_group_in_use,option_value_in_use. - Deleting a product removes its variants (was a 500); a product with orders is refused.
- Explicit
nullclears a variant's barcode, compare-at price, cost and lead time. - Product specs: a bad value
is 422
invalid_spec_value(was 400). - Updating a product no longer drops sale, shipping, custom label and custom number fields.
- Security: suggested products, variant reorder, image edits and variant option values are checked against the store and product in the path.
- Removed:
DELETE /dev/products, which wiped every store's catalogue for any signed-in user.
Categories
GET /stores/{store_id}/categories:Page, every category by default; filter withparent_idorroot_only.- Create answers 201 and needs
categories.write(wascategories.read); create and update no longer fail with a 500. - Adding a product to a category
answers 204 (was
{message}). - New error codes:
category_not_found,category_has_children. Explicitnullclearsdescriptionand the parent. GET /taxonomy/search:Page(was{categories}), no token needed.GET /taxonomy/by-idreturns one category or 404taxonomy_category_not_found.
Collections
GET /collection-products→GET /stores/{store_id}/collections/{collection_id}/products. Old path still answers.Page: collections, collection products ({product_id, title, position}rows, were ids), a product's collections (collection rows, were ids).- Create answers 201;
adding a product
answers 204 (was
{message}); unknown ids are 404collection_not_found. Explicitnullclearsdescription. - Security: updating can no longer touch another store's collection.
Product feeds
/product-feeds,/product-feeds/{id}and/product-feeds/{id}/generate→/stores/{store_id}/product-feeds/.... Old paths still answer./product-feed-overrides→/stores/{store_id}/product-feeds/{feed_id}/overrides. Old path still answers.Page: feeds, overrides. Creates answer 201.- The feed
channelis snake_case. New error codesfeed_not_found,override_not_found. - Security: overrides for another store's product are refused.
Files
GET /tenants/{tenant_id}/files:Page(was{files, total, limit, offset}).Page: folder files, search, connected products.- Delete answers 204 (was 200); creating a folder answers 201.
- New error codes:
file_not_found,folder_not_found,folder_exists,file_not_image,invalid_image, 413file_too_large. - Explicit
nullmoves a file out of its folder.
Inventory
- Store-owned routes moved from
?store_id=or a bodystore_idto/stores/{store_id}/...:/inventory-locations,/stock,/stock/overview,/stock/export,/stock/movements,/stock/bulk-adjust,/stock/bulk-reorder,/reservations,/stock-counts. Old paths still answer. /stock/levels/{id}→GET /stores/{store_id}/variants/{variant_id}/stock;/stock/history/{id}→.../stock/movements. Old paths still answer./stock/adjustand/stock/transfer→POST .../variants/{variant_id}/stock/adjustand.../transfer;/stock/reorder→PUT .../stock/reorder(now PUT). Old paths still answer./stock-items,/component-inventory,/inventory-eventsand/product-variants/{id}/set-stockstay at the root only, undocumented.Pagefor every list (were bare arrays,{items, total, limit, offset},{events, total},{movements, ...}or{levels}).- A stock count is flattened:
{count, items}→ the count's fields plusitems. - Creates answer 201 with the resource (were
{id}); updates return the resource (were 204). - Release and
consume return the
reservation and stock (were
{ok: true}). - Movement filters
item_id,from,to→variant_id,created_after,created_before;created_atis RFC 3339 (was epoch milliseconds). reason,kindandstatusare enums: an unknown reason is 422 (was stored ascorrection).- A negative adjustment beyond on-hand stock is 409
insufficient_stock(was clamped); the transfer codeinsufficient_availablebecameinsufficient_stock. - Stock writes are 409
logical_stock_readonlyon kits and 409inventory_managed_externallyon stores whose stock an ERP owns (unlessforce). - An inventory location's address is plain text (was JSON-encoded).
- Security:
/stock-itemsand/component-inventorylisted every store's stock; adjustments and counts accepted other stores' variants and locations. Both are scoped to the store now.
Purchasing
/suppliers,/purchase-ordersand/stock/reorder-draft→/stores/{store_id}/suppliers,/stores/{store_id}/purchase-orders,/stores/{store_id}/stock/reorder-draft. Old paths still answer.Pagefor lists; creates answer 201 with the resource.- A purchase order is
flattened:
{order, lines, receipts}→ the order's fields pluslinesandreceipts. - Security: purchase orders, receipts and reorder drafts refuse other stores' suppliers and variants.
Manufacturing
/componentsand/build-jobs→/stores/{store_id}/components,/stores/{store_id}/build-jobs. Old paths still answer./product-variants/{id}/components→/stores/{store_id}/kits/{variant_id}/bom. Old path still answers.Pagefor lists; creates answer 201 with the resource.- Security: component creation and bill-of-materials lines refuse other stores' variants.
Shipping boxes
/shipping-boxes→/stores/{store_id}/shipping-boxes. Old path still answers./product-variants/{id}/shipping-boxes→/stores/{store_id}/variants/{variant_id}/shipping-boxes. Old path still answers.- Box dimensions are numbers (were decimal strings).
- Security: a variant's boxes refuse another store's box.
Integrations
- ERP sync logs:
Page(was{logs}) and readable only in their own store. - An unsupported ERP provider is 422
unsupported_erp_provider(was 400); a missing token isprovider_token_missing. - Security: creating or updating an ERP provider masks its secrets (they were echoed back).
Orders
GET /stores/{store_id}/orders: alwaysPage(was a bare array or{orders, total, limit, offset}).statusis an enum (unknown values 400).- List rows drop
freight_quote_id,freight_snapshot_json,freight_external_order_codeand the NF-e fields; read them from the order. - Get,
create and
status change return the
full order (shipments, refunds,
refundable_cents,allowed_status_transitions, ...); create answers 201. PATCH .../statusenforces the lifecycle: 409invalid_status_transition.shipped,delivered,confirmedandprocessingare no longer accepted (422); the codestatus_not_settableis gone.POST .../orders/manualanswers 201. Errors carry codes: 422invalid_manual_discount,invalid_shipping,invalid_postal_code,pix_intent_failed; 404variant_not_found; 409insufficient_stock,insufficient_component_stock(extra fields moved underdetails)./order-item-adjustments→/stores/{store_id}/orders/{order_id}/adjustments. The old path is removed (it always failed with a 500).- Removed:
/fulfillmentsand/fulfillments/{id}(always failed with a 500). - Security:
POST /ordersrefuses other stores' variants, customers and sales channels; a status change checks the store before releasing stock.
Carts
GET /stores/{store_id}/carts:Page;statusis an enum and rows gainkind.GET .../carts/{cart_id}returns the cart's fields withitems(was{cart, items}).
Quotes
GET /stores/{store_id}/quotes:Page(was{quotes}, capped at 200).- Quote
statusisopen,expiredorconverted(wasaberta,expirada,convertida). - Create answers 201. Errors carry
codes: 422
empty_quote,invalid_max_installments,invalid_discount; 409insufficient_stock; 404variant_not_found,customer_not_found,sales_channel_not_found. - Security: a quote refuses another store's customer and sales channel.
Shipments
Page: an order's shipments, unshipped items, freight cancel reasons.- Errors carry codes: 409
no_freight_order, 422nfe_required, andfreight_*codes (422, 404 or 502). - Quoting a shipment takes
invoice_amount_cents;invoice_amountis deprecated.
Payments
POST .../orders/{order_id}/refundanswers 201 with the refund (was{ok, intent_id, ..., provider_response}).- Refunds:
amount_centsbelow 1 is 422; above the refundable balance is 422refund_exceeds_refundable(was silently capped); no approved payment is 409no_refundable_payment. - Refunded orders are marked
refundedorpartially_refunded; the payment status gainspartially_refunded. - Your
Idempotency-Keyon a refund is forwarded to Mercado Pago. GET /stores/{store_id}/payment-providers:Page.- Creating or
updating a provider
masks its secrets; a duplicate is 409
payment_provider_exists. - Security:
GET /payment-providers?store_id=still answers at the root, but masks secrets (it leaked them) and needs the manage permission. - Removed (they always failed with a 500):
/refunds(seeGET /stores/{store_id}/refunds),/payments,/payments/{id}/status,POST /payment-providers. - Removed: the unsigned
/webhooks/payments. Payment gateway webhooks are not served under/v1.
Freight
- Freight providers:
Page; a duplicate is 409freight_provider_exists; cargo types errors carry codes. - Security: creating or updating a provider masks its secrets (carrier tokens were echoed).
- Delivery zones:
Page; create answers 201 with the zone (was{id}); update answers 200 with the zone (was 204).
Locations
GET /stores/{store_id}/locations:Page.- 409
location_slug_takenon a duplicate slug; 409location_in_useon delete.
Discounts
/discount-codes,/discount-codes/{id}and/discount-codes/validate→/stores/{store_id}/discount-codes/...;store_idleaves the body and query. Old paths still answer.- Discount codes:
Page; create answers 201;expires_atis a date-time (bad input 422, was a server error); 409discount_code_exists,discount_code_in_use. - Validate answers
discount_type: null(was"unknown") and areasonenum. - Automatic discounts:
Page;kindandcondition_payment_methodare enums. - Explicit
nullclears nullable fields on update, for codes and automatic discounts.
Sales channels
/sales-channelsand/sales-channels/{id}→/stores/{store_id}/sales-channels/.... Old paths still answer.Page; create answers 201;channel_typeis an enum.- 409 codes:
web_channel_exists,web_channel_protected(was a bare 409),sales_channel_in_use. - Generate API key
answers
{key, prefix}.
Reviews
GET /stores/{store_id}/reviews:Page(was{items, total, pending_count}).PATCH .../reviews/{review_id}requiresstatus(an enum; 422 otherwise).
Customers
GET /stores/{store_id}/customers:Page(was{customers, total, page, per_page, vip_threshold_cents});pageandper_pageare gone;lifecycleis an enum filter.- Customer search also matches full name, company, document and phone digits.
GET .../customers/{customer_id}dropsrecent_orders;ordersholds the 50 newest.- Create answers 201; a
duplicate e-mail is 409
customer_email_exists; unknown ids are 404customer_not_found. - Interactions:
Page; 404 for a customer of another store. /customer-addressesand/customer-addresses/{id}are removed (they failed on every call); useGET .../customers/{customer_id}/addresses(Page).Page: timeline (was{events}), duplicates (was{groups}).- Merge answers
{into, removed, tables}(nook); merging a customer into itself is 422same_customer. - Erase answers
{erased, skipped}(nook). - The CRM endpoints (graph, consent, merge, timeline, duplicates, export,
erase) answer 403
missing_permission(was an empty 403). - E-mail activity
of an unknown customer is 404 (was
{summary: null, events: []}).
Conversations
GET /stores/{store_id}/conversations:Page(was{conversations, counts}); counts moved toGET .../conversations/counts.- The list's
q→searchandchannel→channel_kind;status,priorityandsortare enums (allis no longer accepted); open conversations are no longer listed first. - Messages:
Page, newest first;beforetakes a message id (was a timestamp). - Sending a message
answers 201; 422
empty_message,message_too_long,whatsapp_window_closed(were 400 or a later failure). - Notes answer 201; deleting an unknown note is 404 (was 204).
- Socket ticket
answers 201
{ticket, expires_in}. - Bulk update
answers
{updated}(nook); an unknown action or value is 422. GET .../cartreturns the cart or 404no_cart(was{cart}); item endpoints return the cart (were{cart}or{cart, conversation}).PUT .../cart/customerreturns the state only; reset answers 204; send and PIX answer 201.- Cart errors carry codes:
no_cart,empty_cart,insufficient_stock,variant_unavailable,missing_info,checkout_failed,pix_failed,freight_quote_failed,freight_option_invalid. - Cart search:
q→search;Page(was{variants}). - A WhatsApp message accepted by Meta is
sent(wasdelivered). - Security:
PATCHrefuses another store's customer and assignees outside the store's team (422); typing events are limited to the store's conversations.
Conversation settings
Page: channels, canned replies. Creates answer 201.- Deleting an unknown channel or canned reply is 404 (was 204).
GETandPUT .../conversations-aiuse flat bot settings (was{settings, flags}).
Knowledge base
Page: collections, articles;q→search.- Creates answer 201; deleting an unknown collection or article is 404 (was 204).
- Security: an article refuses another store's collection.
Page: templates, broadcasts, broadcast recipients.- Segments:
Page(was{segments, fields}); fields moved toGET .../email/segments/fields. - Segment members:
Page(was{count, members}, capped at 500). Page: forms (was{forms}), products (was{products}).- Product subscriptions:
Page(was{summary, subscriptions}); counts moved to.../summary. - Template test send answers
204 and fails with 502
email_send_failed(was{ok}). - Send broadcast answers 202 with the campaign; cancel answers 200 with it.
provider,inbox_mode,modeandmatchare enums: unknown values are 422 (were ignored).- New 409 codes:
broadcast_in_flight,broadcast_sending,broadcast_not_sendable,broadcast_not_cancellable,email_form_slug_taken. - New 422 codes:
no_unopened_recipients,csv_missing_file,csv_empty,csv_missing_email_column. Assistant features: 503assistant_unavailable, 502assistant_error. - Deleting an unknown template, form or segment is 404 (was 204); an unknown broadcast is 404 (was 409).
- Form sign-ups and CSV imports of new e-mails were silently lost; they are stored now.
- Security: broadcasts refuse another store's template, and forms another store's welcome template.
- Segment CSV export neutralizes spreadsheet formulas.
Transactional email
GET .../email-templates:Page(was a bare array).- Test and
settings test sends
answer 204 and fail with 502
email_send_failed(were 200{ok}, 422{ok: false}or 502{error: "send_failed"}). - Resetting an unknown event is 404.
Email deliverability
Page: domains (was{domains, region}), suppressions (was{suppressions}).- Adding a domain
answers 201; 409
email_domain_taken; 502ses_error. - Adding a suppression answers 201 with the row (was 204).
- Deleting an unknown domain or suppression is 404.
Ads
- Audiences:
Page(was{audiences, meta_configured}); the flag moved toGET .../ads/connection. - Create answers 201 with the
audience (was
{id, name, status}); sync returns the audience (was{ok, count}) and fails with 502meta_sync_failed. - Deleting an unknown audience is 404.
- Security: performance errors no longer include the Meta access token.
Workflows
Page: workflows, a workflow's runs, versions, secrets.- Store runs:
Page(was{runs, total, limit, offset}). - Fire and
retry answer 202 with the
run (were
{run_id}). - Saving a secret
returns its summary (was
{name}). kindand the settablestatusare enums (unknown values 422, were 400).- Publish with errors is 422
workflow_invalidwithdetails.problems(was{problems}). - New codes: 409
workflow_not_published(resume),run_not_active(cancel),run_active(retry),run_not_awaiting_approval(approval); 422run_not_retryable. GET .../runs/{run_id}is 404 for a run of another workflow.- Restoring a version works (it always failed with a 500).
- Security: fire and
test node refuse another
store's
customer_id(422).
Assistant
/assistant/chat,/assistant/conversations(list),/assistant/prefs,/assistant/usage,/assistant/actions,/assistant/actions/{id}/undo,/assistant/phones,/assistant/phones/verify-startand/assistant/phones/verify-complete→/stores/{store_id}/assistant/.... Old paths still answer.store_idleaves the chat body.Page: conversations (?search=;?q=still accepted), actions (was the 100 newest), phones.- 429
assistant_cap_reached(was plain text); 503assistant_unavailable; 403not_a_memberormissing_permission. - Undo: 409
action_already_undone, 422undo_not_supported(were text). - Renaming a conversation returns it (was 204); stop always answers 204 (was 202 when nothing was running).
- Verify start
answers
{phone_e164, expires_in_seconds}and fails with 502whatsapp_send_failed(was 200{ok: false, message}). - Verify complete
returns the linked phone (was
{verified: true}); a wrong code is 422invalid_verification_code(was 400).
Notifications
Page: channels, logs and inbox (were bare arrays capped at 200 or 100; the inbox takes?unread=).- Alert subscriptions:
Page(was{subscriptions}). - Channel test
answers 204 (was an empty 200) and fails with 502
notification_send_failed(was plain text). PUT .../alert-subscriptions/{user_id}returns the subscription (was{ok: true}); 404 for a non-member; unknown events are 422 (were dropped).channel_type, alert events and alert channel types are enums (unknown values 422); empty alert destinations are 422 (were skipped).- Marking read an unknown log is 404 (was 204).
- Security: channel secrets read as
"__stored__"(they were returned tomarketing.read); sending that value back keeps the stored secret.
Brand
- Errors carry codes (were plain text or bare statuses): 503
assistant_unavailable; 422brand_sources_missing,brand_system_missing,file_missing,invalid_package,invalid_asset; 404brand_job_not_found; 403missing_permission. - Export without a brand
system is 404
brand_system_missing. - Asset upload answers 201 (was 200).
- Job records:
kindandstatusare enums;started_atis a timestamp;already_runningis omitted when false. PUT .../brand-kitanswers{kit, font_presets}likeGET(was{kit}); import fails with 422brand_import_failed.
Storefront
GET /stores/{store_id}/pages:Page(was a bare array); create answers 201 (was 200).- Pages: a duplicate slug is 409
page_slug_taken; blank or too long fields are 422 (were 500). - Security: pages require
cms.readorcms.write(any signed-in user could read and edit any store's pages). - Deployments:
Page(was the 20 newest as a bare array);statusandtriggered_byare enums. - Promote: 409
deployment_not_readyorstorefront_repo_not_connected(were plain text). POST /preview-auth/grant:expires_atis RFC 3339 (was a Unix timestamp).- Domains:
Page; add answers 201 and no longer fails with a 500; 422invalid_hostnameorreserved_suffix(were 400). - Setting a primary domain
that is not verified is 409
domain_not_verified. - GitHub repos:
Page.
Content
Page: CMS collections, CMS documents.
Analytics
GET /dashboard/stats?store_id=→GET /stores/{store_id}/dashboard/stats;GET /dashboard/attention?store_id=→GET /stores/{store_id}/dashboard/attention. Old paths still answer.- Dashboard stats:
revenue_this_month(a float) →revenue_this_month_cents, itself now deprecated in favour ofrevenue_centsandrevenue_change_pct. - Dashboard revenue counts paid orders only:
revenue_seriesno longer includes unpaid orders. - Dashboard days follow the store's time zone (returned as
timezone):7d,30dand90dare whole local days ending today. - Dashboard stats and every tracking report answer 500 on a database error
(they returned zeros or empty lists); the tracking config
PATCHno longer reports success when the write failed. Page: top pages, top referrers, top events (were bare arrays with?limitup to 200).- Recent events:
Pagewith a cursor (was a bare array paged by?before=). - Visitors:
Page; search is?search=(?q=still accepted). The list was always empty (a query error); it now returns visitors. - Conversions:
Page(was{deliveries, total, limit, offset});capi_meta_statusandgoogle_ads_statusare enums (sending,sent,failed,skipped), and the skip reason moved togoogle_ads_skip_reason. - Insights:
touchis an enum; unknown values are 400 (were read aslast_touch). - An unknown visitor is 404
visitor_not_found(was an empty 200). - Resend: 422
order_not_captured(was{"error": "not_captured", ...}). - Catalog sync: 422
meta_catalog_not_configured, 502meta_catalog_error;resultisnullwhen nothing was pushed (was the text"no active variants").
Storefront API
- Shopper endpoints keep their paths, requests and responses; deployed
themes work unchanged.
/v1also answers on store domains. - Security:
POST /storefront/ordersaccepts only the channel's own store's products and variants. - The retired theme checkout
POST /storefront-api/{store_slug}/checkout(always 410) is not served under/v1.