# /api/billing/action (dashboard)

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

Source: https://docs.minds.sh/docs/api/platform/reference/dashboard--api-billing-action



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/billing/action`                                                          |
| 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]

No explicit authentication check in this route module. Imported handlers or deployment middleware may impose additional controls; this inventory does not assert public deployment access.

## 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.

### ActionPayload [#actionpayload]

```typescript
interface ActionPayload {
  action: 'checkout' | 'portal' | 'cancel' | 'reactivate'
  tenantId?: string
  customerId?: string
  subscriptionId?: string
  plan?: string
  successUrl?: string
  cancelUrl?: string
  returnUrl?: string
}
```

Source: `minds-ui/apps/app/app/api/billing/action/route.ts:21`.

## 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) {
  let payload: ActionPayload
  try {
    payload = await request.json()
  } catch {
    return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })
  }

  const { action, tenantId, plan, successUrl, cancelUrl } = payload

  if (!action) {
    return NextResponse.json({ error: 'Missing action' }, { status: 400 })
  }

  if (!tenantId) {
    return NextResponse.json({ error: 'Missing tenantId' }, { status: 400 })
  }

  try {
    switch (action) {
      case 'checkout': {
        if (!plan || !successUrl || !cancelUrl) {
          return NextResponse.json(
            { error: 'checkout requires plan, successUrl, cancelUrl' },
            { status: 400 }
          )
        }

        const session = await createCheckoutSession({
          tenantId,
          planCode: plan,
          successUrl,
          cancelUrl,
        })

        return NextResponse.json({ url: session.url, sessionId: session.sessionId })
      }

      case 'portal': {
        if (!payload.returnUrl) {
          return NextResponse.json(
            { error: 'portal requires returnUrl' },
            { status: 400 }
          )
        }

        const portal = await createPortalSession({
          tenantId,
          returnUrl: payload.returnUrl,
        })

        return NextResponse.json({ url: portal.url })
      }

      case 'cancel': {
        await cancelSubscription({
          tenantId,
          atPeriodEnd: true,
        })

        return NextResponse.json({ success: true })
      }

      case 'reactivate': {
        await resumeSubscription({ tenantId })

        return NextResponse.json({ success: true })
      }

      default:
        return NextResponse.json(
          { error: `Unknown action: ${action}` },
          { status: 400 }
        )
    }
  } catch (error) {
    console.error(`Billing action '${action}' failed:`, error)
    return NextResponse.json(
      { error: error instanceof Error ? error.message : 'Billing operation failed' },
      { status: 500 }
    )
  }
}
```

Source: `minds-ui/apps/app/app/api/billing/action/route.ts:32`.

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

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

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

Source: `minds-ui/apps/app/app/api/billing/action/route.ts:35`.

```typescript
NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })
```

Source: `minds-ui/apps/app/app/api/billing/action/route.ts:37`.

```typescript
NextResponse.json({ error: 'Missing action' }, { status: 400 })
```

Source: `minds-ui/apps/app/app/api/billing/action/route.ts:43`.

```typescript
NextResponse.json({ error: 'Missing tenantId' }, { status: 400 })
```

Source: `minds-ui/apps/app/app/api/billing/action/route.ts:47`.

```typescript
NextResponse.json(
            { error: 'checkout requires plan, successUrl, cancelUrl' },
            { status: 400 }
          )
```

Source: `minds-ui/apps/app/app/api/billing/action/route.ts:54`.

```typescript
NextResponse.json({ url: session.url, sessionId: session.sessionId })
```

Source: `minds-ui/apps/app/app/api/billing/action/route.ts:67`.

```typescript
NextResponse.json(
            { error: 'portal requires returnUrl' },
            { status: 400 }
          )
```

Source: `minds-ui/apps/app/app/api/billing/action/route.ts:72`.

```typescript
NextResponse.json({ url: portal.url })
```

Source: `minds-ui/apps/app/app/api/billing/action/route.ts:83`.

```typescript
NextResponse.json({ success: true })
```

Source: `minds-ui/apps/app/app/api/billing/action/route.ts:92`.

```typescript
NextResponse.json({ success: true })
```

Source: `minds-ui/apps/app/app/api/billing/action/route.ts:98`.

```typescript
NextResponse.json(
          { error: `Unknown action: ${action}` },
          { status: 400 }
        )
```

Source: `minds-ui/apps/app/app/api/billing/action/route.ts:102`.

```typescript
NextResponse.json(
      { error: error instanceof Error ? error.message : 'Billing operation failed' },
      { status: 500 }
    )
```

Source: `minds-ui/apps/app/app/api/billing/action/route.ts:109`.

## 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/billing/action/route.ts`. SHA-256: `7234d84a3ae167c19f0553a1084c5fae34b244ed7fa71d34f8c4fd5ac562264f`.
