Service Management API

Comprehensive API for managing services and subscription plans within organizations, including CRUD operations, plan management, and service limits enforcement.

Updated Dec 16, 2025
Edit on GitHub
services subscriptions plans limits

Service Management

Comprehensive API for managing services and subscription plans within organizations, including CRUD operations, plan management, and service limits enforcement.

Overview

Services are applications that belong to an organization and use AuthOS for authentication. Each service has:

  • Client ID: Unique identifier for OAuth2 flows
  • Service Type: Categorization (web, mobile, desktop, api)
  • OAuth Scopes: Custom scopes for GitHub, Google, and Microsoft providers
  • Redirect URIs: Allowed callback URLs for OAuth2 flows
  • Device Activation URI: Optional URI for device flow authentication
  • Plans: Subscription tiers with pricing and features
  • Usage Tracking: Monitor plan and subscription counts

Organizations have service limits based on their tier. When a service is created, a default “Free” plan is automatically provisioned.

Data Models

Service Model

{
  "id": "uuid",
  "org_id": "uuid",
  "slug": "unique-service-identifier",
  "name": "Service Name",
  "service_type": "web|mobile|desktop|api",
  "client_id": "uuid",
  "github_scopes": ["user:email", "read:user"],
  "microsoft_scopes": ["openid", "profile", "email"],
  "google_scopes": ["openid", "email", "profile"],
  "redirect_uris": ["https://app.example.com/callback"],
  "device_activation_uri": "https://app.example.com/device",
  "created_at": "datetime"
}

Plan Model

{
  "id": "uuid",
  "service_id": "uuid",
  "name": "Plan Name",
  "price_cents": 1999,
  "currency": "usd",
  "features": ["feature1", "feature2"],
  "stripe_price_id": "price_1234567890 (optional)",
  "created_at": "datetime"
}

Service with Details

{
  "id": "uuid",
  "org_id": "uuid",
  "slug": "main-app",
  "name": "Main Application",
  "service_type": "web",
  "client_id": "550e8400-e29b-41d4-a716-446655440000",
  "github_scopes": ["user:email"],
  "microsoft_scopes": ["openid", "email"],
  "google_scopes": ["openid", "email"],
  "redirect_uris": ["https://app.example.com/callback"],
  "device_activation_uri": null,
  "created_at": "2025-01-15T10:30:00Z",
  "plan_count": 3,
  "subscription_count": 150
}

Endpoints Summary

Method Path Description Permissions
POST /api/organizations/:org_slug/services Create new service Owner/Admin
GET /api/organizations/:org_slug/services List organization services Member
GET /api/organizations/:org_slug/services/:service_slug Get service details Member
PATCH /api/organizations/:org_slug/services/:service_slug Update service Owner/Admin
DELETE /api/organizations/:org_slug/services/:service_slug Delete service Owner
POST /api/organizations/:org_slug/services/:service_slug/plans Create plan Owner/Admin
GET /api/organizations/:org_slug/services/:service_slug/plans List service plans Member
PATCH /api/organizations/:org_slug/services/:service_slug/plans/:plan_id Update plan Owner/Admin
DELETE /api/organizations/:org_slug/services/:service_slug/plans/:plan_id Delete plan Owner/Admin

Service Operations

POST /api/organizations/:org_slug/services

Create a new service with automatic plan provisioning.

Permissions: Owner or Admin

Headers:

Header Value
Authorization Bearer {jwt}

Path Parameters:

Parameter Type Description
org_slug string Organization slug

Request Body:

Field Type Required Description
slug string Yes Unique URL-friendly service identifier
name string Yes Service display name
service_type string Yes Service type: web, mobile, desktop, or api
github_scopes string[] No GitHub OAuth scopes (e.g., ["user:email", "read:user"])
microsoft_scopes string[] No Microsoft OAuth scopes (e.g., ["openid", "email", "profile"])
google_scopes string[] No Google OAuth scopes (e.g., ["openid", "email", "profile"])
redirect_uris string[] No Allowed OAuth callback URLs
device_activation_uri string No Device flow activation page URL

Example Request:

curl -X POST https://sso.example.com/api/organizations/acme-corp/services \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "main-app",
    "name": "Main Application",
    "service_type": "web",
    "github_scopes": ["user:email", "read:user"],
    "microsoft_scopes": ["openid", "email", "profile"],
    "google_scopes": ["openid", "email", "profile"],
    "redirect_uris": ["https://app.acme.com/callback"],
    "device_activation_uri": "https://app.acme.com/device"
  }'

Example Response (200 OK):

{
  "service": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "org_id": "org-uuid",
    "slug": "main-app",
    "name": "Main Application",
    "service_type": "web",
    "client_id": "client-uuid",
    "github_scopes": ["user:email", "read:user"],
    "microsoft_scopes": ["openid", "email", "profile"],
    "google_scopes": ["openid", "email", "profile"],
    "redirect_uris": ["https://app.acme.com/callback"],
    "device_activation_uri": "https://app.acme.com/device",
    "created_at": "2025-01-15T10:30:00Z"
  },
  "default_plan": {
    "id": "plan-uuid",
    "service_id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Free",
    "price_cents": 0,
    "currency": "usd",
    "features": [],
    "created_at": "2025-01-15T10:30:00Z"
  },
  "usage": {
    "current_services": 1,
    "max_services": 5,
    "tier": "Free Tier"
  }
}

Error Responses:

  • 400 Bad Request: Invalid service type, service limit reached, or validation error
  • 401 Unauthorized: Invalid or missing JWT
  • 403 Forbidden: User is not an owner or admin, or organization not active
  • 404 Not Found: Organization not found
  • 500 Internal Server Error: Database error

Notes:

  • Valid service types: web, mobile, desktop, api
  • A unique client_id is automatically generated
  • A default “Free” plan is automatically created
  • Service count is checked against organization tier limits
  • An audit log entry is created
  • Scopes and redirect URIs are optional but recommended for OAuth flows
  • Organization must be in active status

Service Type Guidelines:

  • web: Browser-based web applications
  • mobile: iOS and Android native apps
  • desktop: Native desktop applications (Windows, macOS, Linux)
  • api: Backend services and APIs

GET /api/organizations/:org_slug/services

List all services for an organization with usage statistics.

Permissions: Member

Headers:

Header Value
Authorization Bearer {jwt}

Path Parameters:

Parameter Type Description
org_slug string Organization slug

Query Parameters:

Parameter Type Required Description
service_type string No Filter by service type: web, mobile, desktop, api
limit integer No Max services to return (default: no limit)
offset integer No Number of services to skip (default: 0)

Example Request:

curl -X GET "https://sso.example.com/api/organizations/acme-corp/services?service_type=web&limit=20&offset=0" \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Example Response (200 OK):

{
  "services": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "org_id": "org-uuid",
      "slug": "main-app",
      "name": "Main Application",
      "service_type": "web",
      "client_id": "client-uuid",
      "github_scopes": ["user:email"],
      "microsoft_scopes": ["openid", "email"],
      "google_scopes": ["openid", "email"],
      "redirect_uris": ["https://app.acme.com/callback"],
      "device_activation_uri": null,
      "created_at": "2025-01-15T10:30:00Z",
      "plan_count": 3,
      "subscription_count": 150
    },
    {
      "id": "another-service-uuid",
      "org_id": "org-uuid",
      "slug": "mobile-app",
      "name": "Mobile Application",
      "service_type": "mobile",
      "client_id": "another-client-uuid",
      "github_scopes": null,
      "microsoft_scopes": ["openid"],
      "google_scopes": ["openid"],
      "redirect_uris": ["myapp://callback"],
      "device_activation_uri": "https://app.acme.com/activate",
      "created_at": "2025-01-16T14:20:00Z",
      "plan_count": 2,
      "subscription_count": 75
    }
  ],
  "usage": {
    "current_services": 2,
    "max_services": 5,
    "tier": "Free Tier"
  }
}

Error Responses:

  • 401 Unauthorized: Invalid or missing JWT
  • 403 Forbidden: User is not a member
  • 404 Not Found: Organization not found
  • 500 Internal Server Error: Database error

Notes:

  • Returns all services the user has access to within the organization
  • Each service includes plan count and active subscription count
  • Usage information shows current service count vs. tier limits
  • Results can be filtered by service type
  • Pagination supported via limit and offset

GET /api/organizations/:org_slug/services/:service_slug

Get detailed information about a specific service.

Permissions: Member

Headers:

Header Value
Authorization Bearer {jwt}

Path Parameters:

Parameter Type Description
org_slug string Organization slug
service_slug string Service slug

Example Request:

curl -X GET https://sso.example.com/api/organizations/acme-corp/services/main-app \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Example Response (200 OK):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "org_id": "org-uuid",
  "slug": "main-app",
  "name": "Main Application",
  "service_type": "web",
  "client_id": "client-uuid",
  "github_scopes": ["user:email", "read:user"],
  "microsoft_scopes": ["openid", "email", "profile"],
  "google_scopes": ["openid", "email", "profile"],
  "redirect_uris": ["https://app.acme.com/callback"],
  "device_activation_uri": "https://app.acme.com/device",
  "created_at": "2025-01-15T10:30:00Z"
}

Error Responses:

  • 401 Unauthorized: Invalid or missing JWT
  • 403 Forbidden: User is not a member
  • 404 Not Found: Organization or service not found
  • 500 Internal Server Error: Database error

Notes:

  • Returns full service configuration
  • All members can view service details
  • Use this endpoint to retrieve OAuth configuration for integrations

PATCH /api/organizations/:org_slug/services/:service_slug

Update service configuration.

Permissions: Owner or Admin

Headers:

Header Value
Authorization Bearer {jwt}

Path Parameters:

Parameter Type Description
org_slug string Organization slug
service_slug string Service slug

Request Body:

Field Type Required Description
name string No New service name
service_type string No New service type: web, mobile, desktop, api
github_scopes string[] No Updated GitHub OAuth scopes
microsoft_scopes string[] No Updated Microsoft OAuth scopes
google_scopes string[] No Updated Google OAuth scopes
redirect_uris string[] No Updated redirect URIs
device_activation_uri string No Updated device activation URI

Example Request:

curl -X PATCH https://sso.example.com/api/organizations/acme-corp/services/main-app \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Main Application (Production)",
    "redirect_uris": ["https://app.acme.com/callback", "https://app.acme.com/oauth/callback"]
  }'

Example Response (200 OK):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "org_id": "org-uuid",
  "slug": "main-app",
  "name": "Main Application (Production)",
  "service_type": "web",
  "client_id": "client-uuid",
  "github_scopes": ["user:email", "read:user"],
  "microsoft_scopes": ["openid", "email", "profile"],
  "google_scopes": ["openid", "email", "profile"],
  "redirect_uris": ["https://app.acme.com/callback", "https://app.acme.com/oauth/callback"],
  "device_activation_uri": "https://app.acme.com/device",
  "created_at": "2025-01-15T10:30:00Z"
}

Error Responses:

  • 400 Bad Request: Invalid service type or no fields to update
  • 401 Unauthorized: Invalid or missing JWT
  • 403 Forbidden: User is not an owner or admin
  • 404 Not Found: Organization or service not found
  • 500 Internal Server Error: Database error

Notes:

  • All fields are optional - only provided fields are updated
  • Service slug and client_id cannot be changed
  • Valid service types: web, mobile, desktop, api
  • At least one field must be provided
  • Scopes and redirect URIs are completely replaced (not merged)
  • Changes take effect immediately for new authentication flows

DELETE /api/organizations/:org_slug/services/:service_slug

Delete a service and all associated data.

Permissions: Owner only

Headers:

Header Value
Authorization Bearer {jwt}

Path Parameters:

Parameter Type Description
org_slug string Organization slug
service_slug string Service slug

Example Request:

curl -X DELETE https://sso.example.com/api/organizations/acme-corp/services/main-app \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Example Response (204 No Content):

No response body.

Error Responses:

  • 400 Bad Request: Service has active subscriptions (must be cancelled first)
  • 401 Unauthorized: Invalid or missing JWT
  • 403 Forbidden: User is not the owner
  • 404 Not Found: Organization or service not found
  • 500 Internal Server Error: Database error

Notes:

  • This is a destructive operation that cannot be undone
  • Service cannot be deleted if it has active subscriptions
  • Cascading deletes remove: plans, API keys, OAuth tokens, login events
  • Only organization owners can delete services
  • All users will lose access immediately

Plan Management

POST /api/organizations/:org_slug/services/:service_slug/plans

Create a new subscription plan for a service.

Permissions: Owner or Admin

Headers:

Header Value
Authorization Bearer {jwt}

Path Parameters:

Parameter Type Description
org_slug string Organization slug
service_slug string Service slug

Request Body:

Field Type Required Description
name string Yes Plan name (e.g., “Pro”, “Enterprise”)
price_cents integer Yes Price in cents (e.g., 1999 for $19.99)
currency string Yes Currency code (e.g., “usd”, “eur”)
features string[] No List of features included in the plan

Example Request:

curl -X POST https://sso.example.com/api/organizations/acme-corp/services/main-app/plans \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Pro",
    "price_cents": 1999,
    "currency": "usd",
    "features": ["Advanced analytics", "Priority support", "Custom branding"]
  }'

Example Response (200 OK):

{
  "plan": {
    "id": "plan-uuid",
    "service_id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Pro",
    "price_cents": 1999,
    "currency": "usd",
    "features": ["Advanced analytics", "Priority support", "Custom branding"],
    "created_at": "2025-01-15T10:30:00Z"
  },
  "subscription_count": 0
}

Error Responses:

  • 401 Unauthorized: Invalid or missing JWT
  • 403 Forbidden: User is not an owner or admin
  • 404 Not Found: Organization or service not found
  • 500 Internal Server Error: Database error

Notes:

  • Price is stored in cents to avoid floating-point precision issues
  • Features array is optional and can be empty
  • New plans start with zero subscriptions
  • Currency should be ISO 4217 code (lowercase)
  • Plans can be used immediately after creation

GET /api/organizations/:org_slug/services/:service_slug/plans

List all plans for a service.

Permissions: Member

Headers:

Header Value
Authorization Bearer {jwt}

Path Parameters:

Parameter Type Description
org_slug string Organization slug
service_slug string Service slug

Example Request:

curl -X GET https://sso.example.com/api/organizations/acme-corp/services/main-app/plans \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Example Response (200 OK):

[
  {
    "plan": {
      "id": "free-plan-uuid",
      "service_id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Free",
      "price_cents": 0,
      "currency": "usd",
      "features": [],
      "created_at": "2025-01-15T10:30:00Z"
    },
    "subscription_count": 350
  },
  {
    "plan": {
      "id": "pro-plan-uuid",
      "service_id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Pro",
      "price_cents": 1999,
      "currency": "usd",
      "features": ["Advanced analytics", "Priority support", "Custom branding"],
      "created_at": "2025-01-15T11:00:00Z"
    },
    "subscription_count": 75
  }
]

Error Responses:

  • 401 Unauthorized: Invalid or missing JWT
  • 403 Forbidden: User is not a member
  • 404 Not Found: Organization or service not found
  • 500 Internal Server Error: Database error

Notes:

  • Returns all plans for the service
  • Each plan includes active subscription count
  • Plans are not ordered (consider sorting by price or name on client)
  • All members can view plans

PATCH /api/organizations/:org_slug/services/:service_slug/plans/:plan_id

Update a subscription plan.

Permissions: Owner or Admin

Headers:

Header Value
Authorization Bearer {jwt}

Path Parameters:

Parameter Type Description
org_slug string Organization slug
service_slug string Service slug
plan_id string Plan ID (UUID)

Request Body:

Field Type Required Description
name string No New plan name
price_cents integer No New price in cents
currency string No New currency code
features string[] No Updated features list

Example Request:

curl -X PATCH https://sso.example.com/api/organizations/acme-corp/services/main-app/plans/plan-uuid \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "price_cents": 2499,
    "features": ["Advanced analytics", "Priority support", "Custom branding", "API access"]
  }'

Example Response (200 OK):

{
  "plan": {
    "id": "plan-uuid",
    "service_id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Pro",
    "price_cents": 2499,
    "currency": "usd",
    "features": ["Advanced analytics", "Priority support", "Custom branding", "API access"],
    "created_at": "2025-01-15T10:30:00Z"
  },
  "subscription_count": 75
}

Error Responses:

  • 400 Bad Request: No fields to update or plan belongs to different service
  • 401 Unauthorized: Invalid or missing JWT
  • 403 Forbidden: User is not an owner or admin
  • 404 Not Found: Organization, service, or plan not found
  • 500 Internal Server Error: Database error

Notes:

  • All fields are optional - only provided fields are updated
  • At least one field must be provided
  • Features array replaces existing features (not merged)
  • Price changes affect new subscriptions only (existing subscriptions keep their price)
  • Changes take effect immediately

DELETE /api/organizations/:org_slug/services/:service_slug/plans/:plan_id

Delete a subscription plan.

Permissions: Owner or Admin

Headers:

Header Value
Authorization Bearer {jwt}

Path Parameters:

Parameter Type Description
org_slug string Organization slug
service_slug string Service slug
plan_id string Plan ID (UUID)

Example Request:

curl -X DELETE https://sso.example.com/api/organizations/acme-corp/services/main-app/plans/plan-uuid \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Example Response (204 No Content):

No response body.

Error Responses:

  • 400 Bad Request: Plan has active subscriptions (must be cancelled first)
  • 401 Unauthorized: Invalid or missing JWT
  • 403 Forbidden: User is not an owner or admin
  • 404 Not Found: Organization, service, or plan not found
  • 500 Internal Server Error: Database error

Notes:

  • This is a destructive operation that cannot be undone
  • Plan cannot be deleted if it has active subscriptions
  • Consider archiving instead of deleting plans with historical subscriptions
  • Users on this plan will need to be migrated to a different plan first

Security Considerations

Service Limits:

  • Organizations have service limits based on their tier
  • Free tier typically allows 5 services
  • Limits can be customized per organization by Platform Owners
  • Service creation fails if limit is reached

Access Control:

  • Only owners and admins can create/modify services and plans
  • Only owners can delete services
  • All members can view services and plans
  • Organization must be active for service operations

Validation:

  • Service type must be one of: web, mobile, desktop, api
  • Service slug must be unique within the organization
  • Plan price must be non-negative
  • Currency should be valid ISO 4217 code

OAuth Configuration:

  • Redirect URIs should use HTTPS in production
  • Scopes should match provider documentation
  • Device activation URI is optional for CLI/device flows
  • Multiple redirect URIs supported for different environments

Plan Management:

  • Plans with active subscriptions cannot be deleted
  • Price changes don’t affect existing subscriptions
  • Features are informational only (not enforced by the platform)
  • Consider business logic implications before modifying plans

Audit Logging:

  • Service creation logged with all configuration details
  • Service updates and deletions logged
  • Plan modifications logged
  • Actor and timestamp tracked for all operations

Best Practices:

  • Use descriptive service names and slugs
  • Configure appropriate OAuth scopes for your use case
  • Set up separate services for development and production
  • Review subscription counts before deleting plans
  • Test OAuth flows after creating/updating services
  • Use semantic versioning in service names if versioning services