VYG Docs

Migration guide

Move from the retired hosts, routes, tool names, scopes, credentials, list envelopes and field names to the current api.vyg.app contract.

Update existing integrations for the September 2026 API changes: new URLs, renamed routes and tools, a new collection error format, one list format and snake_case field names. Find each replacement below. See the changelog for dates and details.

Hosts

OldNewWhat to do
https://mcp.vyg.apphttps://api.vyg.app/mcpChange the server URL in your MCP client and sign in again.
https://cdp-mcp.vyg.apphttps://api.vyg.app/mcpChange the server URL. vyg_ API keys still work as bearer tokens.
https://cdp.vyg.app read routeshttps://api.vyg.app/v1Move reads to /v1. Collect routes stay on cdp.vyg.app.
https://brand-api.vyg.apphttps://api.vyg.app/v1Change the base URL. brand-api.vyg.app is being retired.
https://cdp.vyg.app/cdp/ingest, /cdp/events, /cdp/vyg.jsUnchangedNothing to change. See Data collection.

The retired MCP hosts no longer resolve. Paths on api.vyg.app/v1 are listed in the REST API reference.

Credentials

  • vyg_ and vyg_ba_ API keys keep working on api.vyg.app with the scopes they carry. See API keys.
  • vyg_live_ keys are not accepted. Requests get 401 with legacy_credential_not_supported. Create a vyg_ba_ key instead.
  • MCP sign-ins made against mcp.vyg.app or cdp-mcp.vyg.app do not carry over. Sign in again against https://api.vyg.app/mcp.
  • Scope names: cdp:read is not a scope. Use the granular read scopes, such as customers:read, commerce:read and insights:read. See Scopes.

MCP server name

The setup guides now name the server vyg. If you added it as liverecover or cdp-mcp, remove those entries and add it again:

claude mcp remove liverecover
claude mcp remove cdp-mcp
claude mcp add --transport http vyg https://api.vyg.app/mcp

Tool names

Tools were renamed on 2026-09-26 to a verb_noun pattern, and campaign tools use "campaign" instead of "workflow". Until 2026-09-27 the old names were still accepted; they are now removed. Calling an old name fails, so update saved prompts, scripts and per-tool approvals or allowlists (for example Claude Code's mcp__vyg__<tool> permissions) to the current names.

Old nameCurrent name
get_brand_infoget_brand
get_brand_insightslist_conversation_insights
get_insight_conversationslist_insight_conversations
conversation-searchsearch_conversations
dashboard-statsget_performance_stats
list_workflowslist_campaigns
get_workflow_detailsget_campaign
compare_workflowscompare_campaigns
customers_listlist_customers
customers_searchsearch_customers
customers_getget_customer
customers_timelineget_customer_activity
customers_orderslist_customer_orders
customers_subscriptionslist_customer_subscriptions
list_traitslist_customer_fields
insights_ltvget_lifetime_value
insights_rfmget_rfm_tiers
get_customer_segmentsget_rfm_tiers
insights_productsget_top_products
insights_at_risklist_at_risk_customers
shopify_admin_graphqlquery_shopify
search_docs_chunkssearch_shopify_docs
validate_graphql_codeblocksvalidate_shopify_graphql
klaviyo_apiquery_klaviyo
klaviyo_search_docssearch_klaviyo_docs

query_shopify and query_klaviyo are no longer listed. Use read_shopify and mutate_shopify, and read_klaviyo and write_klaviyo, which take the same arguments.

get_customer_segments was the 2026-09-26 name of the RFM tool. It is get_rfm_tiers and is separate from saved segments.

Routes, tools and scopes renamed on 2026-09-27

Campaign reads moved from /v1/workflows to /v1/campaigns, performance stats and campaign comparison became GET requests, and every parameter these routes and tools take is snake_case. There are no aliases: the old paths answer 404, and an old argument or query parameter name is refused rather than ignored (400 bad_request over REST, an invalid-arguments error from the MCP tool).

REST routes

OldNew
GET /v1/workflows (status, includeHistorical)GET /v1/campaigns (status, include_historical)
GET /v1/workflows/{workflowId}GET /v1/campaigns/{campaign_id}
POST /v1/workflows/compare (JSON body)GET /v1/campaigns/compare?campaign_ids=&stats=&from=&to=&timezone=
POST /v1/dashboard/stats (JSON body)GET /v1/stats?stats=&from=&to=&compare_from=&compare_to=&campaign_id=&timezone=
GET /v1/search/conversationsGET /v1/conversations/search
GET /v1/customers/search?q=GET /v1/customers/search?query=
GET /v1/traitsGET /v1/customers/fields (same catalog parameter and response)
GET /v1/brand/insights?dayLimit=GET /v1/brand/insights?day_limit=
GET /v1/brand/insights/{brandInsightId}/conversationsGET /v1/brand/insights/{insight_id}/conversations

stats and campaign_ids are lists. Send them as repeated parameters (stats=conversations&stats=aov) or as one comma-separated value (stats=conversations,aov). The OpenAPI operation ids changed with the routes: campaigns.list, campaigns.get, campaigns.compare, stats.get, conversations.search and customers.fields.

Tool arguments

ToolOld argumentNew argument
list_campaignsincludeHistoricalinclude_historical
get_campaignworkflowIdcampaign_id
compare_campaignsworkflowIdscampaign_ids
get_performance_statsworkflowId, compareFrom, compareTocampaign_id, compare_from, compare_to
list_conversation_insightsdayLimitday_limit
list_insight_conversationsbrandInsightIdinsight_id
get_conversation_detailsconversationIdconversation_id
search_customersqquery

Response fields did not change in this rename. They became snake_case on 2026-09-28; see Envelopes and field names changed on 2026-09-28.

Scopes

OldNew
workflows:readcampaigns:read: campaign reads, message variants, and campaign building options (which also accept campaigns:write)
dashboard:readstats:read: performance stats

Sign-ins made before the rename keep their access: a token that carries workflows:read or dashboard:read is read as campaigns:read or stats:read, and refreshing it returns the new names. New sign-ins should ask for the new names; the old ones are no longer advertised. New VYG API keys can also carry these scopes. See API keys and Scopes.

Collect error envelope on 2026-09-28

Errors from the collect endpoints on https://cdp.vyg.app (POST /cdp/events and POST /cdp/ingest) now use the REST API format, {"error": {"code", "message"}}, instead of the flat {"error": "<code>", "error_description": "..."}. Read the code from error.code and the text from error.message. Success responses are unchanged. Error responses with the envelope also carry an X-Request-Id header. Some responses (unserved methods or paths, rate limits and temporary failures) use a {"message": "..."} body, so check the status and read error.code only when the body has an error object.

Old status and errorNew status and code
400 bad_request400 bad_request
400 batch_too_large400 bad_request
400 scope_binding (with rejected)400 bad_request (with rejected next to error)
401 unauthorized401 unauthorized
403 forbidden (also code: beta_not_enabled)403 insufficient_scope
413 payload_too_large413 bad_request
500 internal_error500 internal
503 service_unavailable503 unavailable
503 service_unavailable_partial (accepted, failed)503 unavailable (accepted and failed next to error)

A 503 that carries failed is the old service_unavailable_partial: resend only the events at those indexes. See Collect errors.

Envelopes and field names changed on 2026-09-28

Every list now returns the same envelope, every single record is returned bare, offset paging is replaced by opaque cursors, and every request and response field is snake_case. This covers query parameters, path parameters, request bodies, MCP tool arguments, response keys and the list envelope itself. There are no aliases: an old query parameter, body field or tool argument is refused (400 bad_request over REST, an invalid-arguments error from the MCP tool), and responses carry only the new names. The error envelope, { "error": { "code", "message" } }, is unchanged.

List envelope

Every list returns { data, next_cursor }. next_cursor is null on the last page and always null on a list that returns everything in one page. See Pagination.

EndpointOld envelopeNew envelope
Conversations, messages, contacts, customers, segment history{ data, nextCursor }{ data, next_cursor }
Segment members{ data, nextCursor, generation }{ data, next_cursor, generation }
GET /v1/commerce/orders, GET /v1/commerce/subscriptions{ list, totalSize, offset, pageSize, scope, nextCursor }{ data, next_cursor }
GET /v1/insights/at-risk{ list, totalSize, offset, pageSize, scope, nextCursor }{ data, next_cursor }
GET /v1/commerce/products{ list, pageSize, scope, nextCursor }{ data, next_cursor }
GET /v1/integrations{ list, scope }{ data, next_cursor: null }
GET /v1/event-types{ eventTypes, scope }{ data, next_cursor: null }
GET /v1/customers/fields{ properties, catalogVersion }{ data, next_cursor: null, catalog_version }
GET /v1/conversations/search{ query, filters, results, totalFound }{ data, next_cursor: null }
GET /v1/brand/insights{ insights, totalCount }{ data, next_cursor }
GET /v1/brand/insights/{insight_id}/conversations{ brandInsightId, conversations, totalCount, limit, offset }{ data, next_cursor }
GET /v1/campaigns{ workflows, totalCount }{ data, next_cursor: null }
Providers, event definitions, keys{ data }{ data, next_cursor: null }
GET /v1/segments{ data }{ data, next_cursor }

Customer search (GET /v1/customers/search) returns one page, so its next_cursor is always null. catalog_version is "fields-v1" when you ask for catalog=fields-v1, otherwise null. The dropped envelope keys (totalSize, pageSize, offset, scope, totalCount, totalFound and limit) have no replacement; count the rows you read if you need a total.

Offset paging replaced by cursors

The offset parameter is removed; sending it returns 400 bad_request. Pass the next_cursor of the previous page as cursor, and stop when it is null. Orders, subscriptions and customers at risk still stop at row 10,000: that page has next_cursor: null, and a cursor at or past row 10,000 returns 400.

Endpoint or toolOld parametersNew parameters
GET /v1/commerce/orders, list_orderslimit, offsetlimit, cursor
GET /v1/commerce/subscriptions, list_subscriptionslimit, offsetlimit, cursor
GET /v1/insights/at-risk, list_at_risk_customerslimit, offsetlimit, cursor
GET /v1/brand/insights/{insight_id}/conversations, list_insight_conversationslimit, offsetlimit, cursor
GET /v1/brand/insights, list_conversation_insightslimitlimit, cursor (day_limit as before)

Each endpoint keeps its other filters. Orders take created_at_gte, created_at_lte, state, billing_status, customer_email, customer_phone and customer_id; at-risk customers take as_of.

Single records are no longer wrapped

These operations returned { "data": { ... } } and now return the object itself:

OperationRoute
Get a conversationGET /v1/conversations/{id}
Get a contactGET /v1/contacts/{id}
Get, create and update a providerGET, PATCH /v1/providers/{provider_id}, POST /v1/providers
Get, create and update an event definition/v1/providers/{provider_id}/event-definitions/{definition_id} and POST /v1/providers/{provider_id}/event-definitions
Create, rotate and revoke a keyPOST /v1/keys, POST /v1/keys/{id}/rotate, DELETE /v1/keys/{id}

Replace response.data.id with response.id for these. Other single reads, such as a customer, an order, a segment or a report, were already bare.

Request parameters

WhereOldNew
Provider path parameter/v1/providers/{providerId}/v1/providers/{provider_id}
Event definition path parameter.../event-definitions/{definitionId}.../event-definitions/{definition_id}
/v1/keys query parameter and body fieldkeyClasskey_class (values cdp, brand-api unchanged)
POST /v1/keys bodyfirstKeyfirst_key
GET /v1/customers?sort=, list_customerstotalSpend (default), ordersCount, createdAt, lastOrderAt, lastEventAttotal_spend (default), orders_count, created_at, last_order_at, last_event_at
PATCH /v1/providers/{provider_id} bodyisEnabledis_enabled
Event definition create and update bodypayloadSchema, identifierMappings, isEnabled, schemaVersionpayload_schema, identifier_mappings, is_enabled, schema_version

Tool arguments renamed on 2026-09-28

ToolOld argumentNew argument
read_shopify, mutate_shopify, query_shopifyapiVersionapi_version
learn_shopify_api, search_shopify_docs, validate_shopify_graphqlconversationIdconversation_id
validate_shopify_graphqlcodeblocks[].artifactIdcodeblocks[].artifact_id
list_orders, list_subscriptions, list_at_risk_customers, list_insight_conversationsoffsetcursor

Field names

Timestamps and ids that appear on many resources change the same way everywhere:

OldNew
createdAtcreated_at
updatedAtupdated_at
brandIdbrand_id

The tables below list the other renamed fields by resource. A field that is not listed kept its name.

Conversations, messages and contacts

OldNew
lastEngagedAtlast_engaged_at
lastReceivedAtlast_received_at
awaitingReplyawaiting_reply
cartValuecart_value
cartCurrencycart_currency
checkoutStatecheckout_state
lastMessagelast_message
sentAtsent_at
scheduledForscheduled_for
givenNamegiven_name
familyNamefamily_name
acceptsSmsMarketingaccepts_sms_marketing

Conversation search results (GET /v1/conversations/search, search_conversations):

OldNew
conversationIdconversation_id
textContenttext_content

get_conversation_details (MCP only):

OldNew
goalStateChangesgoal_state_changes
totalPricetotal_price
lineItemsline_items
discountCodesdiscount_codes

Providers and event definitions

OldNew
lookupIdlookup_id
sourceTemplateKeysource_template_key
isEnabledis_enabled
apiKeyapi_key
webhookSecretwebhook_secret
webhookUrlwebhook_url
providerIdprovider_id
payloadSchemapayload_schema
identifierMappingsidentifier_mappings
schemaVersionschema_version

The contents of settings, payload_schema and identifier_mappings are stored as you send them and are not renamed.

API keys

OldNew
keyPrefixkey_prefix
lastUsedAtlast_used_at
expiresAtexpires_at
revokedAtrevoked_at

plaintext on a created or rotated key is unchanged.

Customers

OldNew
ordersCountorders_count
totalSpendtotal_spend
totalRefundedtotal_refunded
lastOrderAtlast_order_at
coverageFromcoverage_from
events30devents_30d
sessions30dsessions_30d
productViews30dproduct_views_30d
lastEventAtlast_event_at
mergedCustomerIdsmerged_customer_ids
firstOrderAtfirst_order_at
readOnlyReasonread_only_reason
catalogVersioncatalog_version

Activity timeline (GET /v1/customers/{id}/timeline, get_customer_activity):

OldNew
eventKeyevent_key
occurredAtoccurred_at
eventTypeevent_type
sourceSystemsource_system
entityKindentity_kind
entityKeyentity_key
productKeyproduct_key
pagePathpage_path
pageTitlepage_title
referrerHostreferrer_host
utmSourceutm_source
utmMediumutm_medium
utmCampaignutm_campaign
sessionIdsession_id

A customer's orders and subscriptions (GET /v1/customers/{id}/orders and /subscriptions, list_customer_orders, list_customer_subscriptions):

OldNew
orderKeyorder_key
subscriptionKeysubscription_key
financialStatusfinancial_status
fulfillmentStatusfulfillment_status
billingIntervalbilling_interval
nextBillingAtnext_billing_at
lineIdline_id
variantKeyvariant_key
unitPriceunit_price

sourceSystem and productKey change as in the timeline table.

Segments

OldNew
currentVersionIdcurrent_version_id
createdBycreated_by
evaluationStatusevaluation_status
memberCountmember_count
evaluatedAtevaluated_at
lastRunAtlast_run_at
currentVersionEvaluatedcurrent_version_evaluated
segmentIdsegment_id
estimatedAtestimated_at
customerIdcustomer_id
lrContactIdlr_contact_id
joinedAtjoined_at
currentCustomerIdcurrent_customer_id
changedAtchanged_at

schemaVersion on a segment is schema_version. The segment definition is stored as you send it and its keys are not renamed. See Segment rules.

Commerce and insights

Orders, subscriptions and at-risk rows were already snake_case; only their envelope changed. On products, images[].altText is images[].alt_text, on the list and on GET /v1/commerce/products/{id}. The LTV, RFM and top-products reports are unchanged and keep scope.

OldNew
altTextalt_text

Brand, stats and conversation insights

GET /v1/brand (get_brand):

OldNew
companyWebsitecompany_website
supportEmailsupport_email
supportPhonesupport_phone
escalationEmailescalation_email
tosUrltos_url
privacyPolicyUrlprivacy_policy_url
faqPageUrlfaq_page_url
reEngagePeriodre_engage_period
planNameplan_name
planPriceplan_price
usageChargePercentageusage_charge_percentage
billingEmailbilling_email
paymentProviderpayment_provider

isEnabled and lookupId on integrations[] are is_enabled and lookup_id.

GET /v1/stats (get_performance_stats) and GET /v1/campaigns/compare (compare_campaigns):

OldNew
currentValuecurrent_value
previousValueprevious_value
comparisonChartcomparison_chart
dateRangedate_range
compareFromcompare_from
compareTocompare_to

get_performance_stats also returns query: the stats, campaign_id and timezone the numbers were computed with. timezone is America/Los_Angeles when the request named none.

Conversation insights (list_conversation_insights, list_insight_conversations):

OldNew
messagePreviewmessage_preview

conversationId, checkoutState and updatedAt change as in the tables above.

Campaigns

GET /v1/campaigns, GET /v1/campaigns/{campaign_id} and GET /v1/campaigns/compare (list_campaigns, get_campaign, compare_campaigns):

OldNew
isPausedis_paused
startsAtstarts_at
endsAtends_at
delayMinutesdelay_minutes
delayTypedelay_type
weekDaysweek_days
specificTimespecific_time
nodeIdnode_id
externalIdexternal_id
agentReplyagent_reply
replyWithMessagereply_with_message
contactSupportcontact_support
flowMessagesflow_messages
knowledgeBaseknowledge_base

compare_campaigns returns the compared campaigns under campaigns (was workflows). supportEmail inside agent_reply is support_email. The contents of goal, flow_messages[].cart.rules and filters[].filter.rule are not renamed. Message variants, discounts, campaign writes and campaign building options were already snake_case.

search_klaviyo_docs (MCP only):

OldNew
resultCountresult_count
operationIdoperation_id
docUrldoc_url
guideTopicsguide_topics
endpointFamiliesendpoint_families

The payloads that read_shopify, mutate_shopify, read_klaviyo and write_klaviyo (and the unlisted query_shopify and query_klaviyo) return come from Shopify and Klaviyo and are not renamed.

Documentation URLs

The documentation moved from /docs, /cdp-api and /mcp to root-level sections on docs.vyg.ai. Old URLs, including those on docs.cdp.vyg.app and docs.mcp.vyg.app, redirect to the new pages.

On this page