Service API

Secure service-to-service endpoints for managing users, subscriptions, and analytics using API key authentication for backend operations.

Updated Nov 22, 2025
Edit on GitHub
service-api api-keys backend subscriptions

Service API (API Key Authentication)

The Service API provides secure service-to-service endpoints for managing users, subscriptions, and analytics using API key authentication. These endpoints enable backend services to perform administrative operations without requiring end-user authentication.

Overview

The Service API enables backend services to:

  • User Management: List, create, read, update, and delete users who have authenticated with your service
  • Subscription Management: Manage user subscriptions and access levels
  • Analytics: Access service-specific usage metrics and statistics
  • Service Configuration: Update service information and settings

All Service API endpoints use API key authentication instead of JWT tokens, making them ideal for server-to-server communication.

Authentication

API Key Authentication

All Service API endpoints require authentication via API key in the X-API-Key header:

curl -X GET https://sso.example.com/api/service/users \
  -H "X-API-Key: sk_live_abc123xyz..."

API Key Format

API keys follow the format: sk_{environment}_{random}

  • sk_test_...: Test/development environment keys
  • sk_live_...: Production environment keys

Obtaining API Keys

API keys are generated via the Organization Management API:

POST /api/organizations/:org_slug/services/:service_slug/api-keys

See API Keys Documentation for details on generating and managing keys.

Permissions Model

Each API key has an associated set of permissions that control access to endpoints:

Permission Description Endpoints Enabled
read:users Read user data GET /api/service/users, GET /api/service/users/:user_id
write:users Create/update users POST /api/service/users, PATCH /api/service/users/:user_id
delete:users Delete users DELETE /api/service/users/:user_id
read:subscriptions Read subscription data GET /api/service/subscriptions, GET /api/service/subscriptions/:user_id
write:subscriptions Create/update subscriptions POST /api/service/subscriptions, PATCH /api/service/subscriptions/:user_id
delete:subscriptions Delete subscriptions DELETE /api/service/subscriptions/:user_id
read:analytics Access analytics GET /api/service/analytics
read:service Read service info GET /api/service/info
write:service Update service info PATCH /api/service/info

Endpoints Summary

Method Path Description Required Permission
GET /api/service/users List users read:users
POST /api/service/users Create user write:users
GET /api/service/users/:user_id Get user details read:users
PATCH /api/service/users/:user_id Update user write:users
DELETE /api/service/users/:user_id Delete user delete:users
GET /api/service/subscriptions List subscriptions read:subscriptions
POST /api/service/subscriptions Create subscription write:subscriptions
GET /api/service/subscriptions/:user_id Get user subscription read:subscriptions
PATCH /api/service/subscriptions/:user_id Update subscription write:subscriptions
DELETE /api/service/subscriptions/:user_id Delete subscription delete:subscriptions
GET /api/service/analytics Get service analytics read:analytics
GET /api/service/info Get service information read:service
PATCH /api/service/info Update service information write:service

User Management

GET /api/service/users

List all users who have authenticated with your service, with pagination support.

Required Permission: read:users

Headers:

Header Value Required
X-API-Key sk_live_... Yes

Query Parameters:

Parameter Type Required Description
limit integer No Maximum results (default: 50, max: 100)
offset integer No Pagination offset (default: 0)

Response (200 OK):

{
  "users": [
    {
      "id": "user_123",
      "email": "john@example.com",
      "created_at": "2025-01-10T08:00:00Z"
    },
    {
      "id": "user_456",
      "email": "jane@example.com",
      "created_at": "2025-01-12T10:30:00Z"
    }
  ],
  "total": 245
}

Example Request:

curl -X GET "https://sso.example.com/api/service/users?limit=20&offset=0" \
  -H "X-API-Key: sk_live_abc123xyz..."

Error Responses:

  • 401 Unauthorized: Missing or invalid API key
  • 403 Forbidden: API key lacks read:users permission

POST /api/service/users

Create a new user or return existing user if email already exists. Useful for provisioning users before they authenticate.

Required Permission: write:users

Headers:

Header Value Required
X-API-Key sk_live_... Yes
Content-Type application/json Yes

Request Body:

Field Type Required Description
email string Yes Valid email address

Example Request:

curl -X POST https://sso.example.com/api/service/users \
  -H "X-API-Key: sk_live_abc123xyz..." \
  -H "Content-Type: application/json" \
  -d '{
    "email": "newuser@example.com"
  }'

Response (200 OK):

{
  "id": "user_789",
  "email": "newuser@example.com",
  "created_at": "2025-01-15T10:30:00Z"
}

Error Responses:

  • 400 Bad Request: Invalid email format
    {
      "error": "Invalid email format",
      "error_code": "BAD_REQUEST"
    }
    
  • 401 Unauthorized: Missing or invalid API key
  • 403 Forbidden: API key lacks write:users permission

Important Notes:

  • If user already exists with this email, returns existing user (idempotent)
  • Does not send verification emails or welcome emails
  • User cannot set a password until they authenticate through another method first
  • User can authenticate immediately via OAuth (if email matches existing OAuth account)
  • Publishes UserSignupSuccess event for new users

User Access After Provisioning

Users created via the Service API are provisioned without passwords and cannot receive email verification. To gain access, they have these options:

If the user’s email matches an existing OAuth account (Google, GitHub, Microsoft):

# User clicks OAuth login button on your application
# System matches email and authenticates successfully

2. Password Reset Flow (Manual Admin Action)

An administrator must trigger a password reset for the provisioned user:

# Admin calls password reset endpoint
POST /api/auth/forgot-password
{
  "email": "provisioned@company.com"
}

# User receives reset email and sets their password

3. Invitation System (Alternative)

Use the invitation system instead of direct provisioning for better user experience:

# Send invitation with password setup
POST /api/organizations/:org_slug/invitations
{
  "email": "user@company.com",
  "role": "member"
}

Best Practice: For new users who need password access, prefer the invitation system over direct provisioning to provide a complete onboarding experience.


GET /api/service/users/:user_id

Get detailed information about a specific user.

Required Permission: read:users

Headers:

Header Value Required
X-API-Key sk_live_... Yes

Path Parameters:

Parameter Type Description
user_id string User ID (UUID)

Response (200 OK):

{
  "id": "user_123",
  "email": "john@example.com",
  "created_at": "2025-01-10T08:00:00Z"
}

Example Request:

curl -X GET https://sso.example.com/api/service/users/user_123 \
  -H "X-API-Key: sk_live_abc123xyz..."

Error Responses:

  • 404 Not Found: User not found or hasn’t authenticated with this service
    {
      "error": "User not found or has not authenticated with this service",
      "error_code": "NOT_FOUND"
    }
    
  • 401 Unauthorized: Missing or invalid API key
  • 403 Forbidden: API key lacks read:users permission

PATCH /api/service/users/:user_id

Update user information. Currently supports updating email address.

Required Permission: write:users

Headers:

Header Value Required
X-API-Key sk_live_... Yes
Content-Type application/json Yes

Path Parameters:

Parameter Type Description
user_id string User ID (UUID)

Request Body:

Field Type Required Description
email string No New email address

Example Request:

curl -X PATCH https://sso.example.com/api/service/users/user_123 \
  -H "X-API-Key: sk_live_abc123xyz..." \
  -H "Content-Type: application/json" \
  -d '{
    "email": "newemail@example.com"
  }'

Response (200 OK):

{
  "id": "user_123",
  "email": "newemail@example.com",
  "created_at": "2025-01-10T08:00:00Z"
}

Error Responses:

  • 400 Bad Request: Invalid email format or email already taken
    {
      "error": "Email already taken by another user",
      "error_code": "BAD_REQUEST"
    }
    
  • 404 Not Found: User not found or hasn’t authenticated with this service
  • 401 Unauthorized: Missing or invalid API key
  • 403 Forbidden: API key lacks write:users permission

DELETE /api/service/users/:user_id

Permanently delete a user and all associated data. This action is irreversible.

Required Permission: delete:users

Headers:

Header Value Required
X-API-Key sk_live_... Yes

Path Parameters:

Parameter Type Description
user_id string User ID (UUID)

Response: 204 No Content

Example Request:

curl -X DELETE https://sso.example.com/api/service/users/user_123 \
  -H "X-API-Key: sk_live_abc123xyz..."

Error Responses:

  • 404 Not Found: User not found or hasn’t authenticated with this service
  • 401 Unauthorized: Missing or invalid API key
  • 403 Forbidden: API key lacks delete:users permission

Important Notes:

  • Deletes all user data including identities, sessions, and audit logs
  • Cannot be undone
  • User can re-authenticate to create a new account

Subscription Management

GET /api/service/subscriptions

List all subscriptions for your service with optional status filtering.

Required Permission: read:subscriptions

Headers:

Header Value Required
X-API-Key sk_live_... Yes

Query Parameters:

Parameter Type Required Description
status string No Filter by status: active, cancelled, expired, past_due
limit integer No Maximum results (default: 50, max: 100)
offset integer No Pagination offset (default: 0)

Response (200 OK):

{
  "subscriptions": [
    {
      "id": "sub_123",
      "user_id": "user_123",
      "plan_id": "plan_pro",
      "plan_name": "Professional Plan",
      "status": "active",
      "current_period_end": "2025-02-15T10:30:00Z"
    },
    {
      "id": "sub_456",
      "user_id": "user_456",
      "plan_id": "plan_basic",
      "plan_name": "Basic Plan",
      "status": "active",
      "current_period_end": "2025-02-20T08:00:00Z"
    }
  ],
  "total": 180
}

Example Request:

curl -X GET "https://sso.example.com/api/service/subscriptions?status=active&limit=20" \
  -H "X-API-Key: sk_live_abc123xyz..."

Error Responses:

  • 401 Unauthorized: Missing or invalid API key
  • 403 Forbidden: API key lacks read:subscriptions permission

POST /api/service/subscriptions

Create a new subscription for a user. Typically used when a user subscribes through your billing system.

Required Permission: write:subscriptions

Headers:

Header Value Required
X-API-Key sk_live_... Yes
Content-Type application/json Yes

Request Body:

Field Type Required Description
user_id string Yes User ID (UUID)
plan_id string Yes Plan ID (UUID)
status string No Subscription status (default: active)
current_period_end string No ISO 8601 datetime (default: 30 days from now)

Example Request:

curl -X POST https://sso.example.com/api/service/subscriptions \
  -H "X-API-Key: sk_live_abc123xyz..." \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "user_123",
    "plan_id": "plan_pro",
    "status": "active",
    "current_period_end": "2025-02-15T10:30:00Z"
  }'

Response (200 OK):

{
  "id": "sub_789",
  "user_id": "user_123",
  "plan_id": "plan_pro",
  "plan_name": "Professional Plan",
  "status": "active",
  "current_period_end": "2025-02-15T10:30:00Z"
}

Error Responses:

  • 400 Bad Request: Invalid plan_id or user_id
  • 403 Forbidden: Plan doesn’t belong to this service
    {
      "error": "Plan does not belong to this service",
      "error_code": "FORBIDDEN"
    }
    
  • 404 Not Found: User or plan not found
  • 401 Unauthorized: Missing or invalid API key
  • 403 Forbidden: API key lacks write:subscriptions permission

Important Notes:

  • User must exist before creating subscription
  • Plan must belong to your service
  • Only one active subscription per user per service
  • Subscription status affects JWT claims

GET /api/service/subscriptions/:user_id

Get subscription details for a specific user.

Required Permission: read:subscriptions

Headers:

Header Value Required
X-API-Key sk_live_... Yes

Path Parameters:

Parameter Type Description
user_id string User ID (UUID)

Response (200 OK):

{
  "id": "sub_123",
  "user_id": "user_123",
  "plan_id": "plan_pro",
  "plan_name": "Professional Plan",
  "status": "active",
  "current_period_end": "2025-02-15T10:30:00Z"
}

Example Request:

curl -X GET https://sso.example.com/api/service/subscriptions/user_123 \
  -H "X-API-Key: sk_live_abc123xyz..."

Error Responses:

  • 404 Not Found: Subscription not found for this user
    {
      "error": "Subscription not found for this user",
      "error_code": "NOT_FOUND"
    }
    
  • 401 Unauthorized: Missing or invalid API key
  • 403 Forbidden: API key lacks read:subscriptions permission

PATCH /api/service/subscriptions/:user_id

Update subscription status or period end date. Used for subscription renewals, cancellations, or status changes.

Required Permission: write:subscriptions

Headers:

Header Value Required
X-API-Key sk_live_... Yes
Content-Type application/json Yes

Path Parameters:

Parameter Type Description
user_id string User ID (UUID)

Request Body:

Field Type Required Description
status string No New status: active, cancelled, expired, past_due
current_period_end string No New period end date (ISO 8601)

Example Request:

curl -X PATCH https://sso.example.com/api/service/subscriptions/user_123 \
  -H "X-API-Key: sk_live_abc123xyz..." \
  -H "Content-Type: application/json" \
  -d '{
    "status": "active",
    "current_period_end": "2025-03-15T10:30:00Z"
  }'

Response (200 OK):

{
  "id": "sub_123",
  "user_id": "user_123",
  "plan_id": "plan_pro",
  "plan_name": "Professional Plan",
  "status": "active",
  "current_period_end": "2025-03-15T10:30:00Z"
}

Error Responses:

  • 404 Not Found: Subscription not found for this user
  • 401 Unauthorized: Missing or invalid API key
  • 403 Forbidden: API key lacks write:subscriptions permission

Common Use Cases:

  • Renewal: Update current_period_end to extend subscription
  • Cancellation: Set status to cancelled
  • Payment failure: Set status to past_due
  • Expiration: Set status to expired

DELETE /api/service/subscriptions/:user_id

Delete a user’s subscription. This permanently removes the subscription record.

Required Permission: delete:subscriptions

Headers:

Header Value Required
X-API-Key sk_live_... Yes

Path Parameters:

Parameter Type Description
user_id string User ID (UUID)

Response: 204 No Content

Example Request:

curl -X DELETE https://sso.example.com/api/service/subscriptions/user_123 \
  -H "X-API-Key: sk_live_abc123xyz..."

Error Responses:

  • 404 Not Found: Subscription not found for this user
  • 401 Unauthorized: Missing or invalid API key
  • 403 Forbidden: API key lacks delete:subscriptions permission

Important Notes:

  • Use PATCH with status: "cancelled" for soft cancellation
  • Use DELETE only for complete removal
  • User can create new subscription later

Analytics

GET /api/service/analytics

Get comprehensive analytics for your service including user counts, subscription metrics, and login activity.

Required Permission: read:analytics

Headers:

Header Value Required
X-API-Key sk_live_... Yes

Response (200 OK):

{
  "total_users": 245,
  "total_subscriptions": 180,
  "active_subscriptions": 165,
  "total_logins_30d": 12450,
  "unique_users_30d": 210
}

Example Request:

curl -X GET https://sso.example.com/api/service/analytics \
  -H "X-API-Key: sk_live_abc123xyz..."

Error Responses:

  • 401 Unauthorized: Missing or invalid API key
  • 403 Forbidden: API key lacks read:analytics permission

Metrics Explained:

  • total_users: All users who have ever authenticated with your service
  • total_subscriptions: All subscriptions regardless of status
  • active_subscriptions: Subscriptions with status active
  • total_logins_30d: Number of login events in last 30 days
  • unique_users_30d: Distinct users who logged in during last 30 days

Use Cases:

  • Dashboard metrics
  • Monitoring service health
  • Tracking growth trends
  • Calculating churn rates

Service Configuration

GET /api/service/info

Get information about your service including name, slug, and type.

Required Permission: read:service

Headers:

Header Value Required
X-API-Key sk_live_... Yes

Response (200 OK):

{
  "id": "service_123",
  "name": "Main Application",
  "slug": "main-app",
  "service_type": "web",
  "created_at": "2025-01-01T00:00:00Z"
}

Example Request:

curl -X GET https://sso.example.com/api/service/info \
  -H "X-API-Key: sk_live_abc123xyz..."

Error Responses:

  • 401 Unauthorized: Missing or invalid API key
  • 403 Forbidden: API key lacks read:service permission

PATCH /api/service/info

Update service configuration. Currently supports updating the service name.

Required Permission: write:service

Headers:

Header Value Required
X-API-Key sk_live_... Yes
Content-Type application/json Yes

Request Body:

Field Type Required Description
name string No New service name

Example Request:

curl -X PATCH https://sso.example.com/api/service/info \
  -H "X-API-Key: sk_live_abc123xyz..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Updated Application Name"
  }'

Response (200 OK):

{
  "id": "service_123",
  "name": "Updated Application Name",
  "slug": "main-app",
  "service_type": "web",
  "created_at": "2025-01-01T00:00:00Z"
}

Error Responses:

  • 401 Unauthorized: Missing or invalid API key
  • 403 Forbidden: API key lacks write:service permission

Security Considerations

API Key Security

Storage:

  • Store API keys in environment variables or secure vaults
  • Never commit API keys to version control
  • Use different keys for test and production environments

Transmission:

  • Always use HTTPS for API requests
  • Never send API keys in URL parameters
  • Use X-API-Key header for authentication

Rotation:

  • Rotate API keys regularly (quarterly recommended)
  • Immediately revoke compromised keys
  • Generate new keys when team members leave

Permission Scope

Principle of Least Privilege:

  • Grant minimum required permissions
  • Use separate keys for different purposes
  • Review and audit key permissions regularly

Rate Limiting

Service API endpoints have the following rate limits:

  • Read operations: 100 requests per minute per API key
  • Write operations: 50 requests per minute per API key
  • Delete operations: 20 requests per minute per API key

Data Access

API keys can only access:

  • Users who have authenticated with the specific service
  • Subscriptions for that service
  • Analytics for that service

Cross-service access is not permitted.


Error Responses

All endpoints return consistent error format:

{
  "error": "Human-readable error message",
  "error_code": "ERROR_CODE_ENUM",
  "timestamp": "2025-01-15T10:30:00Z"
}

Common Error Codes

HTTP Status Error Code Description
400 Bad Request BAD_REQUEST Invalid request parameters
401 Unauthorized UNAUTHORIZED Missing or invalid API key
403 Forbidden FORBIDDEN Insufficient permissions
404 Not Found NOT_FOUND Resource not found
429 Too Many Requests RATE_LIMIT_EXCEEDED Rate limit exceeded
500 Internal Server Error INTERNAL_SERVER_ERROR Server error

Best Practices

Error Handling

Always check HTTP status codes and implement retry logic for transient errors.

Pagination

Always paginate large result sets to avoid timeouts.

Idempotency

User creation is idempotent - safe to call multiple times.

Caching

Cache analytics data but always fetch fresh user/subscription data.