Skip to main content
GET
/
services
Get Service Entitlements
curl --request GET \
  --url https://auth.nullpass.xyz/api/services \
  --header 'Authorization: Bearer <token>'
{
  "entitlements": [
    {
      "id": "<string>",
      "userId": "<string>",
      "service": "DROP",
      "tier": "premium",
      "isPremium": true,
      "accessFlags": {},
      "metadata": {},
      "customStorageLimit": 123,
      "customApiKeyLimit": 123,
      "createdAt": "2023-11-07T05:31:56Z",
      "updatedAt": "2023-11-07T05:31:56Z"
    }
  ]
}

Endpoints

GET /api/services
POST /api/services

GET /api/services

Retrieves service entitlements for the authenticated user. Can be filtered by service type.

Query Parameters

service
string
Optional. Filter by service: DROP, MAILS, VAULT, or DB

Response

entitlements
array
Array of service entitlement objects

POST /api/services

Creates or updates a service entitlement for the authenticated user. Uses upsert logic (creates if doesn’t exist, updates if exists).

Request

service
ServiceIdentifier
required
Service identifier: DROP, MAILS, VAULT, or DB
tier
string
Access tier (e.g., “free”, “premium”, “enterprise”)
isPremium
boolean
Premium access flag
accessFlags
object
Custom access flags (JSON object)
metadata
object
Service-specific metadata (JSON object)
customStorageLimit
number
Custom storage limit in bytes
customApiKeyLimit
number
Custom API key limit
polarCustomerId
string
Polar customer ID
polarSubscriptionId
string
Polar subscription ID
polarSubscriptionStatus
string
Polar subscription status

Response

entitlement
object
Created or updated entitlement object

Implementation Details

Code Reference

export async function GET(request: NextRequest) {
  const corsResponse = handleCors(request)
  if (corsResponse) return corsResponse

  const blocked = await protectRoute(request)
  if (blocked) return blocked

  const auth = await requireAuth(request)
  if ('error' in auth) return auth.error

  try {
    const { searchParams } = new URL(request.url)
    const service = searchParams.get('service') as 'DROP' | 'MAILS' | 'VAULT' | 'DB' | null

    const where: any = { userId: auth.userId }
    if (service) {
      where.service = service
    }

    const entitlements = await prisma.userServiceEntitlement.findMany({
      where,
    })

    return jsonResponse({ entitlements }, 200, request.headers.get('origin'))
  } catch (error) {
    logger.error('Get service access error:', error)
    return errorResponse('Internal server error', 500, request.headers.get('origin'))
  }
}

export async function POST(request: NextRequest) {
  const corsResponse = handleCors(request)
  if (corsResponse) return corsResponse

  const blocked = await protectRoute(request)
  if (blocked) return blocked

  const auth = await requireAuth(request)
  if ('error' in auth) return auth.error

  try {
    const body = await request.json()
    const validated = updateServiceSchema.parse(body)

    logger.ups('Service entitlement update:', auth.userId, validated.service)

    const entitlement = await prisma.userServiceEntitlement.upsert({
      where: {
        userId_service: {
          userId: auth.userId,
          service: validated.service,
        },
      },
      update: {
        ...(validated.tier !== undefined && { tier: validated.tier }),
        ...(validated.isPremium !== undefined && { isPremium: validated.isPremium }),
        ...(validated.accessFlags !== undefined && { accessFlags: validated.accessFlags }),
        ...(validated.metadata !== undefined && { metadata: validated.metadata }),
        ...(validated.customStorageLimit !== undefined && { customStorageLimit: validated.customStorageLimit }),
        ...(validated.customApiKeyLimit !== undefined && { customApiKeyLimit: validated.customApiKeyLimit }),
        ...(validated.polarCustomerId !== undefined && { polarCustomerId: validated.polarCustomerId }),
        ...(validated.polarSubscriptionId !== undefined && { polarSubscriptionId: validated.polarSubscriptionId }),
        ...(validated.polarSubscriptionStatus !== undefined && { polarSubscriptionStatus: validated.polarSubscriptionStatus }),
        updatedAt: new Date(),
      },
      create: {
        userId: auth.userId,
        service: validated.service,
        tier: validated.tier || 'free',
        isPremium: validated.isPremium || false,
        accessFlags: validated.accessFlags || undefined,
        metadata: validated.metadata || undefined,
        customStorageLimit: validated.customStorageLimit || null,
        customApiKeyLimit: validated.customApiKeyLimit || null,
        polarCustomerId: validated.polarCustomerId || null,
        polarSubscriptionId: validated.polarSubscriptionId || null,
        polarSubscriptionStatus: validated.polarSubscriptionStatus || null,
      },
    })

    logger.info('Service entitlement updated:', auth.userId, validated.service)

    return jsonResponse({ entitlement }, 200, request.headers.get('origin'))
  } catch (error: any) {
    if (error.name === 'ZodError') {
      logger.warn('Service update validation error:', error.errors)
      return errorResponse(error.errors[0].message, 400, request.headers.get('origin'))
    }
    logger.error('Update service access error:', error)
    return errorResponse('Internal server error', 500, request.headers.get('origin'))
  }
}

Status Codes

200
OK
Success
400
Bad Request
Validation error
401
Unauthorized
Missing or invalid authentication token

Example Requests

Get All Services

curl -X GET https://auth.nullpass.xyz/api/services \
  -H "Authorization: Bearer YOUR_TOKEN"

Get Specific Service

curl -X GET "https://auth.nullpass.xyz/api/services?service=DROP" \
  -H "Authorization: Bearer YOUR_TOKEN"

Update Service Entitlement

curl -X POST https://auth.nullpass.xyz/api/services \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "service": "DROP",
    "tier": "premium",
    "isPremium": true,
    "customStorageLimit": 10737418240,
    "customApiKeyLimit": 100
  }'

Upsert Behavior

The POST endpoint uses Prisma’s upsert operation:
  • If entitlement exists: Updates only provided fields, preserves others
  • If entitlement doesn’t exist: Creates new entitlement with provided values and defaults
  • Unique constraint: userId + service combination must be unique

Default Values

When creating a new entitlement:
  • tier: “free”
  • isPremium: false
  • connected: true (set by database default)
  • customStorageLimit: null
  • customApiKeyLimit: null
  • Polar fields: null

Authorizations

Authorization
string
header
required

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Query Parameters

service
enum<string>
Available options:
DROP,
MAILS,
VAULT,
DB

Response

200 - application/json

Service entitlements

entitlements
object[]