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",
"description": "Optional plan description",
"price_cents": 1999,
"currency": "usd",
"features": ["feature1", "feature2"],
"stripe_price_id": "price_1234567890 (optional)",
"is_default": false,
"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 |
| POST | /api/organizations/:org_slug/services/:service_slug/checkout |
Create Stripe checkout session | Member |
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 error401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not an owner or admin, or organization not active404 Not Found: Organization not found500 Internal Server Error: Database error
Notes:
- Valid service types:
web,mobile,desktop,api - A unique
client_idis 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 service settings used by documented upstream-login and resource-token paths
- Organization must be in
activestatus
Service Type Guidelines:
- web: Browser-based web applications
- mobile: iOS and Android native applications
- desktop: Native desktop applications
- api: Backend services and APIs
[!IMPORTANT] Upstream login for mobile/desktop services: AuthOS binds a generated PKCE verifier to its stored upstream-provider transaction for these service types. This protects the provider-backed login exchange; it does not expose a general AuthOS authorization-code endpoint or make the service an OpenID Connect public client.
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 JWT403 Forbidden: User is not a member404 Not Found: Organization not found500 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
limitandoffset
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 JWT403 Forbidden: User is not a member404 Not Found: Organization or service not found500 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 update401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not an owner or admin404 Not Found: Organization or service not found500 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 JWT403 Forbidden: User is not the owner404 Not Found: Organization or service not found500 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