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 aprojectIdargument. - 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, oradminrole. - 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.
Deprecation: 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=50Three routes change more than the prefix:
POST /api/contacts/importbecomes the contact-import resource,POST /api/projects/:projectId/contact-imports, with its own request format.GET /api/dashboard/statsbecomesGET /api/projects/:projectId/dashboard/stats.- The live analytics stream at
/api/analytics/streamis served only asGET /api/projects/:projectId/analytics/stream.
Analytics
| Method | Legacy route | Project 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
| Method | Legacy route | Project 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
| Method | Legacy route | Project 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
| Method | Legacy route | Project 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
| Method | Legacy route | Project 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
| Method | Legacy route | Project route |
|---|---|---|
POST | /api/contacts/import | /api/projects/:projectId/contact-imports |
Contacts
| Method | Legacy route | Project 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
| Method | Legacy route | Project route |
|---|---|---|
GET | /api/dashboard/stats | /api/projects/:projectId/dashboard/stats |
Data sources
| Method | Legacy route | Project 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
| Method | Legacy route | Project 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
| Method | Legacy route | Project 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
| Method | Legacy route | Project 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
| Method | Legacy route | Project 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
| Method | Legacy route | Project 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
| Method | Legacy route | Project 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/:projectIdThere 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
projectIdto every data tool, for examplelist_contactsorget_analytics_overview. There is no stored active project and noswitch_projecttool. - An organization API key that omits
projectIdruns in legacy mode against your migrated project and receives thelegacynotice described above. This mode is removed with the HTTP routes. - Project management and grant tools require a delegated user through OAuth; an API key receives
MCP_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
| Status | Code | Meaning |
|---|---|---|
404 | NOT_FOUND | The project does not exist in your organization, or you cannot access it. |
403 | PROJECT_ROLE_REQUIRED | You can access the project, but your role is too low for this operation. |
409 | PROJECT_ARCHIVED | The project is archived and accepts no writes. |
503 | ORGANIZATION_PROVISIONING | The organization's default project is still being prepared. Retry shortly. |
410 | LEGACY_ROUTE_REMOVED | The legacy route was removed. Use the project route from the table above. |
Migration checklist
- Call
GET /api/projectsand record the project ID each integration should use. - Grant each API key the projects and roles it needs.
- Rewrite HTTP paths using the tables above and add
projectIdto MCP tool calls. - Handle
404,403,409, and503as described in Errors. - Confirm your traffic no longer carries
Deprecationheaders or MCPlegacynotices.