# /api/provisioning/instances/upgrade (dashboard)

Source-derived dashboard endpoint contract, authentication, request validation, responses, and errors.

Source: https://docs.minds.sh/docs/api/platform/reference/dashboard--api-provisioning-instances-upgrade



This is the **dashboard** service route. Its origin and authentication are described in [Platform API overview](/docs/api/platform) and [Authentication](/docs/api/platform/authentication). A path shared by another service is a different endpoint.

| Property                       | Contract                                                                       |
| ------------------------------ | ------------------------------------------------------------------------------ |
| Methods                        | POST                                                                           |
| Path                           | `/api/provisioning/instances/upgrade`                                          |
| Path parameters                | None                                                                           |
| Query parameters read in route | None read directly; schema validation below may define additional fields       |
| Headers read in route          | No additional direct header reads; authentication helpers may read credentials |

## Authentication and access [#authentication-and-access]

Dashboard session cookie through @repo/auth/server. Some auth-summary routes can redirect to sign-in; inspect the status branches below.

## Request and response definitions [#request-and-response-definitions]

The following named definitions are imported or declared by this route. Optional markers, defaults, bounds, and enum values are shown exactly as implemented. A response type is a source contract, not an example populated with real account data.

### UpgradeBody [#upgradebody]

```typescript
type UpgradeBody = {
  instanceId?: string;
  renderServiceId?: string;
  renderServiceName?: string;
  plan?: string;
  region?: string;
  hostname?: string;
  deploymentSlug?: string;
};
```

Source: `minds-ui/apps/app/app/api/provisioning/instances/upgrade/route.ts:17`.

## Method behavior [#method-behavior]

The handler excerpt preserves field validation, response envelopes, cookie changes, and exception branches. Values returned by service helpers retain their named response type above; for passthrough routes, the upstream response is authoritative.

### POST [#post]

```typescript
export async function POST(request: NextRequest) {
  const session = await getSession();
  if (!session) {
    return jsonError('UNAUTHENTICATED', 'Sign in before upgrading a Mind.', 401, {
      redirectTo: `/sign-in?returnTo=${encodeURIComponent(RETURN_TO)}`,
    });
  }

  const cookieStore = await cookies();
  const organizationContext = await loadMindsOrganizationContext({
    accessToken: session.accessToken,
    activeTenantId: cookieStore.get(ACTIVE_TENANT_COOKIE)?.value,
    tokenOrgId: session.user.orgId,
    tokenTenantId: session.user.tenantId,
  });
  const organizationId = organizationContext.activeOrganizationId;
  if (!organizationId) {
    return jsonError(
      'NO_ORGANIZATION',
      'Create or select an organization before upgrading.',
      409,
      { redirectTo: '/organizations' }
    );
  }

  try {
    const body = (await request.json().catch(() => ({}))) as UpgradeBody;
    const instanceId = body.instanceId?.trim();
    if (!instanceId) {
      return jsonError('INVALID_UPGRADE_REQUEST', 'instanceId is required.', 400);
    }
    if (body.plan && body.plan !== 'pro') {
      return jsonError(
        'INVALID_UPGRADE_REQUEST',
        'Only Free→Pro in-place upgrades are supported.',
        400
      );
    }

    // Prefer explicit Render id; else resolve by name (instance name / slug).
    let renderServiceId = body.renderServiceId?.trim();
    let renderServiceName = body.renderServiceName?.trim();
    if (!renderServiceId) {
      for (const group of organizationContext.tenantGroups) {
        const match = group.services.find((service) => service.id === instanceId);
        if (match) {
          const metaId = match.metadata?.renderServiceId;
          if (typeof metaId === 'string' && metaId.trim()) {
            renderServiceId = metaId.trim();
          }
          if (!renderServiceName) {
            renderServiceName = match.name || match.slug;
          }
          break;
        }
      }
    }

    const result = await upgradeInstanceToPro({
      instanceId,
      organizationId,
      plan: 'pro',
      renderServiceId,
      renderServiceName: renderServiceName || undefined,
      region: body.region,
      hostname: body.hostname,
      deploymentSlug: body.deploymentSlug,
    });

    // Same instance_id — envelope + warm only.
    return NextResponse.json({ instance: result, sameInstanceId: true });
  } catch (error) {
    if (error instanceof UpgradeRequestError) {
      return jsonError('UPGRADE_FAILED', error.message, error.status);
    }
    console.error('Minds Free→Pro upgrade failed:', error);
    return jsonError(
      'UPGRADE_FAILED',
      error instanceof Error ? error.message : 'Free→Pro upgrade failed.',
      502
    );
  }
}
```

Source: `minds-ui/apps/app/app/api/provisioning/instances/upgrade/route.ts:27`.

## Response and error branches [#response-and-error-branches]

Literal HTTP statuses in the route: delegated or computed. Shared management error classes are defined in [Errors and responses](/docs/api/platform/errors).

```typescript
jsonError('UNAUTHENTICATED', 'Sign in before upgrading a Mind.', 401, {
      redirectTo: `/sign-in?returnTo=${encodeURIComponent(RETURN_TO)}`,
    })
```

Source: `minds-ui/apps/app/app/api/provisioning/instances/upgrade/route.ts:30`.

```typescript
jsonError(
      'NO_ORGANIZATION',
      'Create or select an organization before upgrading.',
      409,
      { redirectTo: '/organizations' }
    )
```

Source: `minds-ui/apps/app/app/api/provisioning/instances/upgrade/route.ts:44`.

```typescript
request.json()
```

Source: `minds-ui/apps/app/app/api/provisioning/instances/upgrade/route.ts:53`.

```typescript
jsonError('INVALID_UPGRADE_REQUEST', 'instanceId is required.', 400)
```

Source: `minds-ui/apps/app/app/api/provisioning/instances/upgrade/route.ts:56`.

```typescript
jsonError(
        'INVALID_UPGRADE_REQUEST',
        'Only Free→Pro in-place upgrades are supported.',
        400
      )
```

Source: `minds-ui/apps/app/app/api/provisioning/instances/upgrade/route.ts:59`.

```typescript
NextResponse.json({ instance: result, sameInstanceId: true })
```

Source: `minds-ui/apps/app/app/api/provisioning/instances/upgrade/route.ts:97`.

```typescript
jsonError('UPGRADE_FAILED', error.message, error.status)
```

Source: `minds-ui/apps/app/app/api/provisioning/instances/upgrade/route.ts:100`.

```typescript
jsonError(
      'UPGRADE_FAILED',
      error instanceof Error ? error.message : 'Free→Pro upgrade failed.',
      502
    )
```

Source: `minds-ui/apps/app/app/api/provisioning/instances/upgrade/route.ts:103`.

```typescript
NextResponse.json({ error: { code, message, ...extra } }, { status })
```

Source: `minds-ui/apps/app/app/api/provisioning/instances/upgrade/route.ts:117`.

## Service dependencies [#service-dependencies]

* `minds-ui/apps/app/lib/organizations.ts`
* `minds-ui/apps/app/lib/provisioning/upgrade.ts`

## Evidence [#evidence]

Generated from the local source snapshot on 2026-09-12. This page documents implemented code and does not certify a deployed service, permissions configuration, or successful provider operation.

Source file: `minds-ui/apps/app/app/api/provisioning/instances/upgrade/route.ts`. SHA-256: `9dcea44da91cfa4703818b86e16761a6ace064237ba68edf1ac13cc9a0e1bb71`.
