Migration guide · 98 legacy routes

Migrate to projects

Contacts, campaigns, segments, templates, assets, data sources, tracking clients, analytics, insights, and automations now belong to a project inside your organization. The organization keeps members, billing, and API keys. Organization-scoped routes keep working for a limited time; move your integrations to the project-scoped routes below before they are removed.

What changes

  • Every product request names its project: HTTP paths start with/api/projects/:projectId/, and MCP data tools take a projectId argument.
  • Your existing data moved, unchanged, into one project per organization. That project is fixed: legacy routes always read and write it, even if you later make another project the default.
  • Access is granted per project. Organization owners reach every project; other members and API keys need a project grant with the viewer, editor, or admin role.
  • Studio URLs include the project slug, for example /studio/:projectSlug/contacts.
  • Tracking snippets and SDKs keep working without changes.

Deprecation and sunset

While a legacy route still works, every response carries three headers. The dates are fixed for your environment and do not move:

Deprecation: @<enablement time, Unix seconds>
Sunset: <fixed HTTP date, at least 90 days after enablement>
Link: <https://usereachout.com/docs/project-migration>; rel="deprecation"; type="text/html"

MCP tools called in legacy mode return the same information as a structured legacy object with deprecatedAt, sunsetAt, migrationGuide,operationId, and projectId.

Legacy routes are removed only after the Sunset date and 14 consecutive days in which no legacy route was used successfully. Any successful call restarts that 14-day window. After removal, a legacy HTTP route returns 410 LEGACY_ROUTE_REMOVED, and a legacy MCP call returns the structuredLEGACY_ROUTE_REMOVED error without running the operation.

Watch for the headers. If your client logs response headers, alert onDeprecation: it tells you a call still uses a legacy route and which operation to move next.

Find your project ID

List the projects your credentials can access:

GET /api/projects
Authorization: Bearer <api_key>
{
  "projects": [
    { "id": "project_…", "slug": "legacy-…", "name": "…", "status": "active", "isDefault": true }
  ]
}

Organization owners see every project; an API key or member sees only the projects it has a grant for. Use the stable id in API paths; the slug is for Studio URLs. Over MCP, delegated users call list_projects, and organization API keys call get_organization_overview.

HTTP routes

For almost every route, insert /projects/:projectId after /api and keep the same method. Each project-scoped request checks that the project exists in your organization and that your credential has a sufficient role on it.

- GET /api/contacts?limit=50
+ GET /api/projects/project_…/contacts?limit=50

Three routes change more than the prefix:

  • POST /api/contacts/import becomes the contact-import resource,POST /api/projects/:projectId/contact-imports, with its own request format.
  • GET /api/dashboard/stats becomes GET /api/projects/:projectId/dashboard/stats.
  • The live analytics stream at /api/analytics/stream is served only asGET /api/projects/:projectId/analytics/stream.

Analytics

MethodLegacy routeProject route
GET/api/analytics/campaigns/:campaignId/performance/api/projects/:projectId/analytics/campaigns/:campaignId/performance
GET/api/analytics/campaigns/performance/api/projects/:projectId/analytics/campaigns/performance
GET/api/analytics/countries/api/projects/:projectId/analytics/countries
GET/api/analytics/devices/api/projects/:projectId/analytics/devices
GET/api/analytics/email/api/projects/:projectId/analytics/email
GET/api/analytics/engaged-users/api/projects/:projectId/analytics/engaged-users
GET/api/analytics/events/api/projects/:projectId/analytics/events
GET/api/analytics/geo/api/projects/:projectId/analytics/geo
GET/api/analytics/identification-diagnostic/api/projects/:projectId/analytics/identification-diagnostic
GET/api/analytics/overview/api/projects/:projectId/analytics/overview
GET/api/analytics/pages/api/projects/:projectId/analytics/pages
GET/api/analytics/realtime/api/projects/:projectId/analytics/realtime
GET/api/analytics/referrers/api/projects/:projectId/analytics/referrers
GET/api/analytics/refs/api/projects/:projectId/analytics/refs
GET/api/analytics/stream/api/projects/:projectId/analytics/stream

Assets

MethodLegacy routeProject route
GET/api/assets/api/projects/:projectId/assets
POST/api/assets/api/projects/:projectId/assets
DELETE/api/assets/:assetId/api/projects/:projectId/assets/:assetId
GET/api/assets/:assetId/api/projects/:projectId/assets/:assetId
PATCH/api/assets/:assetId/api/projects/:projectId/assets/:assetId

Automations

MethodLegacy routeProject route
GET/api/automations/api/projects/:projectId/automations
POST/api/automations/api/projects/:projectId/automations
DELETE/api/automations/:automationId/api/projects/:projectId/automations/:automationId
PATCH/api/automations/:automationId/api/projects/:projectId/automations/:automationId
PATCH/api/automations/:automationId/toggle/api/projects/:projectId/automations/:automationId/toggle

Campaigns

MethodLegacy routeProject route
GET/api/campaigns/api/projects/:projectId/campaigns
POST/api/campaigns/api/projects/:projectId/campaigns
DELETE/api/campaigns/:campaignId/api/projects/:projectId/campaigns/:campaignId
GET/api/campaigns/:campaignId/api/projects/:projectId/campaigns/:campaignId
PATCH/api/campaigns/:campaignId/api/projects/:projectId/campaigns/:campaignId
POST/api/campaigns/:campaignId/cancel/api/projects/:projectId/campaigns/:campaignId/cancel
POST/api/campaigns/:campaignId/preview-email/api/projects/:projectId/campaigns/:campaignId/preview-email
GET/api/campaigns/:campaignId/progress/api/projects/:projectId/campaigns/:campaignId/progress
POST/api/campaigns/:campaignId/send/api/projects/:projectId/campaigns/:campaignId/send
GET/api/campaigns/:campaignId/stats/api/projects/:projectId/campaigns/:campaignId/stats
POST/api/campaigns/:campaignId/test/api/projects/:projectId/campaigns/:campaignId/test
GET/api/campaigns/overview/api/projects/:projectId/campaigns/overview

Contact fields

MethodLegacy routeProject route
GET/api/contact-fields/api/projects/:projectId/contact-fields
POST/api/contact-fields/api/projects/:projectId/contact-fields
DELETE/api/contact-fields/:fieldId/api/projects/:projectId/contact-fields/:fieldId
PATCH/api/contact-fields/:fieldId/api/projects/:projectId/contact-fields/:fieldId

Contact imports

MethodLegacy routeProject route
POST/api/contacts/import/api/projects/:projectId/contact-imports

Contacts

MethodLegacy routeProject route
GET/api/contacts/api/projects/:projectId/contacts
POST/api/contacts/api/projects/:projectId/contacts
DELETE/api/contacts/:contactId/api/projects/:projectId/contacts/:contactId
GET/api/contacts/:contactId/api/projects/:projectId/contacts/:contactId
PATCH/api/contacts/:contactId/api/projects/:projectId/contacts/:contactId
POST/api/contacts/bulk-delete/api/projects/:projectId/contacts/bulk-delete
POST/api/contacts/bulk-update/api/projects/:projectId/contacts/bulk-update
GET/api/contacts/dedup/api/projects/:projectId/contacts/dedup
POST/api/contacts/dedup/api/projects/:projectId/contacts/dedup
POST/api/contacts/dedup/resolve/api/projects/:projectId/contacts/dedup/resolve
POST/api/contacts/search/api/projects/:projectId/contacts/search

Dashboard stats

MethodLegacy routeProject route
GET/api/dashboard/stats/api/projects/:projectId/dashboard/stats

Data sources

MethodLegacy routeProject route
GET/api/data-sources/api/projects/:projectId/data-sources
POST/api/data-sources/api/projects/:projectId/data-sources
DELETE/api/data-sources/:dataSourceId/api/projects/:projectId/data-sources/:sourceId
GET/api/data-sources/:dataSourceId/api/projects/:projectId/data-sources/:sourceId
PATCH/api/data-sources/:dataSourceId/api/projects/:projectId/data-sources/:sourceId
GET/api/data-sources/:dataSourceId/tables/api/projects/:projectId/data-sources/:sourceId/tables
GET/api/data-sources/:dataSourceId/tables/:tableName/fields/api/projects/:projectId/data-sources/:sourceId/tables/:collection/fields
GET/api/data-sources/:dataSourceId/tables/:tableName/rows/api/projects/:projectId/data-sources/:sourceId/tables/:collection/rows

Email domains

MethodLegacy routeProject route
GET/api/campaigns/domains/api/projects/:projectId/campaigns/domains
POST/api/campaigns/domains/api/projects/:projectId/campaigns/domains
DELETE/api/campaigns/domains/:domainId/api/projects/:projectId/campaigns/domains/:domainId
GET/api/campaigns/domains/:domainId/api/projects/:projectId/campaigns/domains/:domainId
PATCH/api/campaigns/domains/:domainId/api/projects/:projectId/campaigns/domains/:domainId
POST/api/campaigns/domains/:domainId/verify/api/projects/:projectId/campaigns/domains/:domainId/verify

Insights

MethodLegacy routeProject route
GET/api/insights/api/projects/:projectId/insights
POST/api/insights/definitions/api/projects/:projectId/insights/definitions
GET/api/insights/definitions/:definitionId/preview/api/projects/:projectId/insights/definitions/:definitionId/preview
POST/api/insights/definitions/:definitionId/run/api/projects/:projectId/insights/definitions/:definitionId/run
GET/api/insights/reports/:reportId/api/projects/:projectId/insights/reports/:reportId
POST/api/insights/run-due/api/projects/:projectId/insights/run-due

Recipient list binding

MethodLegacy routeProject route
GET/api/recipient-list-binding/api/projects/:projectId/recipient-list-binding
POST/api/recipient-list-binding/api/projects/:projectId/recipient-list-binding
POST/api/recipient-list-binding/lock/api/projects/:projectId/recipient-list-binding/lock
POST/api/recipient-list-binding/unlock/api/projects/:projectId/recipient-list-binding/unlock
POST/api/recipient-list-binding/validate/api/projects/:projectId/recipient-list-binding/validate

Segments

MethodLegacy routeProject route
GET/api/segments/api/projects/:projectId/segments
POST/api/segments/api/projects/:projectId/segments
DELETE/api/segments/:segmentId/api/projects/:projectId/segments/:segmentId
GET/api/segments/:segmentId/api/projects/:projectId/segments/:segmentId
PATCH/api/segments/:segmentId/api/projects/:projectId/segments/:segmentId
GET/api/segments/:segmentId/contacts/api/projects/:projectId/segments/:segmentId/contacts
POST/api/segments/:segmentId/estimate/api/projects/:projectId/segments/:segmentId/estimate
POST/api/segments/estimate/api/projects/:projectId/segments/estimate

Templates

MethodLegacy routeProject route
GET/api/templates/api/projects/:projectId/templates
POST/api/templates/api/projects/:projectId/templates
DELETE/api/templates/:templateId/api/projects/:projectId/templates/:templateId
GET/api/templates/:templateId/api/projects/:projectId/templates/:templateId
PATCH/api/templates/:templateId/api/projects/:projectId/templates/:templateId
POST/api/templates/:templateId/preview/api/projects/:projectId/templates/:templateId/preview

Tracking clients

MethodLegacy routeProject route
GET/api/tracking-clients/api/projects/:projectId/tracking-clients
POST/api/tracking-clients/api/projects/:projectId/tracking-clients
DELETE/api/tracking-clients/:trackingClientId/api/projects/:projectId/tracking-clients/:trackingClientId
PATCH/api/tracking-clients/:trackingClientId/api/projects/:projectId/tracking-clients/:trackingClientId
POST/api/tracking-clients/:trackingClientId/rotate-secret/api/projects/:projectId/tracking-clients/:trackingClientId/rotate-secret

API keys

Every API key that existed at migration received the admin role on your migrated project, so existing integrations keep their access to it. Keys are not granted new projects automatically: grant each one explicitly.

POST /api/api-keys/:keyId/projects
{ "projectId": "project_…", "role": "editor" }

PATCH  /api/api-keys/:keyId/projects/:projectId   { "role": "viewer" }
DELETE /api/api-keys/:keyId/projects/:projectId

There is no all-projects grant, and linking a key to a user never widens what the key can reach. Over MCP, a delegated user manages grants with grant_api_key_project, list_api_key_project_grants, and revoke_api_key_project.

MCP

  • Pass projectId to every data tool, for examplelist_contacts or get_analytics_overview. There is no stored active project and no switch_project tool.
  • An organization API key that omits projectId runs in legacy mode against your migrated project and receives the legacy notice described above. This mode is removed with the HTTP routes.
  • Project management and grant tools require a delegated user through OAuth; an API key receivesMCP_USER_DELEGATION_REQUIRED. See the MCP reference.

Tracking

No change is needed. Snippets and SDKs keep sending their tracking client ID, and ReachOut resolves the project from it. A tracking client cannot move between projects once it has received events; to track a site in another project, create a new tracking client there and install its snippet.

Errors

StatusCodeMeaning
404NOT_FOUNDThe project does not exist in your organization, or you cannot access it.
403PROJECT_ROLE_REQUIREDYou can access the project, but your role is too low for this operation.
409PROJECT_ARCHIVEDThe project is archived and accepts no writes.
503ORGANIZATION_PROVISIONINGThe organization's default project is still being prepared. Retry shortly.
410LEGACY_ROUTE_REMOVEDThe legacy route was removed. Use the project route from the table above.

Migration checklist

  1. Call GET /api/projects and record the project ID each integration should use.
  2. Grant each API key the projects and roles it needs.
  3. Rewrite HTTP paths using the tables above and add projectId to MCP tool calls.
  4. Handle 404, 403, 409, and 503 as described in Errors.
  5. Confirm your traffic no longer carries Deprecation headers or MCP legacy notices.