# Phoenix Operating Model - API Specification **Complete API specification for all five control planes** This document provides detailed API specifications for the Phoenix operating model, including GraphQL schemas, REST endpoints, and integration patterns. --- ## Executive Summary The Phoenix operating model exposes APIs for all five control planes: 1. **Commercial Plane API** - Client and billing operations 2. **Tenancy Plane API** - Tenant and identity operations 3. **Subscription Plane API** - Subscription and quota operations 4. **Environment Plane API** - Environment and deployment operations 5. **Content & DevOps Plane API** - Content and Git operations All APIs support: - GraphQL interface (primary) - REST interface (alternative) - Authentication via Keycloak - Authorization via RBAC - Audit logging - Multi-region support --- ## I. Commercial Plane API ### GraphQL Schema ```graphql # Client Management type Client { id: ID! name: String! legalEntity: LegalEntity! contract: Contract msa: MSA invoicingConfig: InvoicingConfig! paymentInstruments: [PaymentInstrument!]! costCenters: [CostCenter!]! tenants: [Tenant!]! usageAggregation: UsageAggregation chargebackRules: [ChargebackRule!]! createdAt: DateTime! updatedAt: DateTime! metadata: JSON } type LegalEntity { name: String! registrationNumber: String jurisdiction: String! taxId: String address: Address! } type InvoicingConfig { format: InvoiceFormat! frequency: InvoiceFrequency! currency: String! paymentTerms: String! billingAddress: Address! emailRecipients: [String!]! } enum InvoiceFormat { PDF XML JSON } enum InvoiceFrequency { MONTHLY QUARTERLY ANNUAL } # Queries type Query { client(id: ID!): Client clients(filter: ClientFilter): [Client!]! billing(clientId: ID!, timeRange: TimeRange!): BillingData! invoices(clientId: ID!, filter: InvoiceFilter): [Invoice!]! costCenters(clientId: ID!): [CostCenter!]! } # Mutations type Mutation { createClient(input: CreateClientInput!): Client! updateClient(id: ID!, input: UpdateClientInput!): Client! deleteClient(id: ID!): Boolean! createInvoice(clientId: ID!, period: BillingPeriod!): Invoice! processPayment(invoiceId: ID!, payment: PaymentInput!): Payment! addPaymentInstrument(clientId: ID!, instrument: PaymentInstrumentInput!): PaymentInstrument! removePaymentInstrument(clientId: ID!, instrumentId: ID!): Boolean! createCostCenter(clientId: ID!, input: CreateCostCenterInput!): CostCenter! updateCostCenter(clientId: ID!, costCenterId: ID!, input: UpdateCostCenterInput!): CostCenter! deleteCostCenter(clientId: ID!, costCenterId: ID!): Boolean! } # Input Types input CreateClientInput { name: String! legalEntity: LegalEntityInput! invoicingConfig: InvoicingConfigInput! contract: ContractInput msa: MSAInput } input ClientFilter { name: String jurisdiction: String status: ClientStatus } input TimeRange { start: DateTime! end: DateTime! } ``` ### REST Endpoints ``` # Client Management POST /api/v1/commercial/clients GET /api/v1/commercial/clients GET /api/v1/commercial/clients/{id} PUT /api/v1/commercial/clients/{id} DELETE /api/v1/commercial/clients/{id} # Billing GET /api/v1/commercial/clients/{id}/billing GET /api/v1/commercial/clients/{id}/invoices POST /api/v1/commercial/clients/{id}/invoices GET /api/v1/commercial/invoices/{id} POST /api/v1/commercial/invoices/{id}/payments # Cost Centers POST /api/v1/commercial/clients/{id}/cost-centers GET /api/v1/commercial/clients/{id}/cost-centers PUT /api/v1/commercial/clients/{id}/cost-centers/{costCenterId} DELETE /api/v1/commercial/clients/{id}/cost-centers/{costCenterId} ``` ### Example Queries #### Create Client ```graphql mutation { createClient(input: { name: "Government Agency A" legalEntity: { name: "Government Agency A" jurisdiction: "Nation A" address: { street: "123 Main St" city: "Capital City" country: "Nation A" } } invoicingConfig: { format: PDF frequency: MONTHLY currency: "USD" paymentTerms: "Net 30" billingAddress: { street: "123 Main St" city: "Capital City" country: "Nation A" } emailRecipients: ["billing@agency.gov"] } }) { id name legalEntity { name jurisdiction } } } ``` #### Get Billing Data ```graphql query { billing( clientId: "client-id" timeRange: { start: "2025-01-01T00:00:00Z" end: "2025-01-31T23:59:59Z" } ) { totalCost currency bySubscription { subscription { id name } cost percentage } byCostCenter { costCenter { id name } cost percentage } } } ``` --- ## II. Tenancy Plane API ### GraphQL Schema ```graphql # Tenant Management type Tenant { id: ID! name: String! primaryDomains: [String!]! identityProvider: IdentityProvider! rbacNamespace: String! dataResidencyFlags: [DataResidencyFlag!]! complianceProfile: ComplianceProfile! client: Client! subscriptions: [Subscription!]! environments: [Environment!]! regions: [Region!]! keycloakRealmId: String multiRegionEnabled: Boolean! regionalDataResidency: [RegionalDataResidency!]! crossBorderGovernance: CrossBorderGovernance createdAt: DateTime! updatedAt: DateTime! metadata: JSON } type IdentityProvider { type: IdentityProviderType! config: JSON! ssoEnabled: Boolean! mfaRequired: Boolean! } enum IdentityProviderType { KEYCLOAK AZURE_AD OKTA GOOGLE_WORKSPACE CUSTOM_SAML CUSTOM_OIDC } # Queries type Query { tenant(id: ID!): Tenant tenants(filter: TenantFilter): [Tenant!]! tenantByDomain(domain: String!): Tenant identityProvider(tenantId: ID!): IdentityProvider complianceProfile(tenantId: ID!): ComplianceProfile } # Mutations type Mutation { createTenant(input: CreateTenantInput!): Tenant! updateTenant(id: ID!, input: UpdateTenantInput!): Tenant! deleteTenant(id: ID!): Boolean! configureIdentityProvider(tenantId: ID!, provider: IdentityProviderInput!): IdentityProvider! syncKeycloakRealm(tenantId: ID!): KeycloakRealm! updateComplianceProfile(tenantId: ID!, profile: ComplianceProfileInput!): ComplianceProfile! addDataResidencyFlag(tenantId: ID!, flag: DataResidencyFlagInput!): DataResidencyFlag! removeDataResidencyFlag(tenantId: ID!, flagId: ID!): Boolean! } # Input Types input CreateTenantInput { name: String! clientId: ID! primaryDomains: [String!]! identityProvider: IdentityProviderInput! rbacNamespace: String! complianceProfile: ComplianceProfileInput! dataResidencyFlags: [DataResidencyFlagInput!] multiRegionEnabled: Boolean } ``` ### REST Endpoints ``` # Tenant Management POST /api/v1/tenancy/tenants GET /api/v1/tenancy/tenants GET /api/v1/tenancy/tenants/{id} PUT /api/v1/tenancy/tenants/{id} DELETE /api/v1/tenancy/tenants/{id} GET /api/v1/tenancy/tenants/by-domain/{domain} # Identity Provider GET /api/v1/tenancy/tenants/{id}/identity-provider PUT /api/v1/tenancy/tenants/{id}/identity-provider POST /api/v1/tenancy/tenants/{id}/keycloak/sync # Compliance GET /api/v1/tenancy/tenants/{id}/compliance PUT /api/v1/tenancy/tenants/{id}/compliance ``` ### Example Queries #### Create Tenant ```graphql mutation { createTenant(input: { name: "agency-tenant" clientId: "client-id" primaryDomains: ["agency.gov", "agency.sankofa.nexus"] identityProvider: { type: KEYCLOAK config: {} ssoEnabled: true mfaRequired: true } rbacNamespace: "agency-namespace" complianceProfile: { standards: [ISO_27001, SOC_2, FEDRAMP] } dataResidencyFlags: [{ region: "region-a" requirement: REQUIRED enforcement: HARD }] multiRegionEnabled: true }) { id name primaryDomains keycloakRealmId } } ``` #### Configure Identity Provider ```graphql mutation { configureIdentityProvider( tenantId: "tenant-id" provider: { type: AZURE_AD config: { tenantId: "azure-tenant-id" clientId: "azure-client-id" clientSecret: "azure-client-secret" } ssoEnabled: true mfaRequired: true } ) { type ssoEnabled mfaRequired } } ``` --- ## III. Subscription Plane API ### GraphQL Schema ```graphql # Subscription Management type Subscription { id: ID! name: String! tenant: Tenant! client: Client! type: SubscriptionType! serviceBundles: [ServiceBundle!]! quotas: Quotas! limits: Limits! costTracking: CostTracking! policyPacks: [PolicyPack!]! featureEntitlements: [FeatureEntitlement!]! environments: [Environment!]! regions: [Region!]! createdAt: DateTime! updatedAt: DateTime! metadata: JSON } enum SubscriptionType { SHARED_PLATFORM PRODUCT SANDBOX INNOVATION } type ServiceBundle { service: ServiceType! enabled: Boolean! quotas: ServiceQuotas limits: ServiceLimits } enum ServiceType { COMPUTE STORAGE NETWORKING DATABASE AI_ML ANALYTICS SECURITY MONITORING BACKUP } # Queries type Query { subscription(id: ID!): Subscription subscriptions(filter: SubscriptionFilter): [Subscription!]! quotas(subscriptionId: ID!): Quotas! checkQuota(subscriptionId: ID!, resource: ResourceType!): QuotaStatus! } # Mutations type Mutation { createSubscription(input: CreateSubscriptionInput!): Subscription! updateSubscription(id: ID!, input: UpdateSubscriptionInput!): Subscription! deleteSubscription(id: ID!): Boolean! updateQuotas(subscriptionId: ID!, quotas: QuotasInput!): Quotas! addServiceBundle(subscriptionId: ID!, bundle: ServiceBundleInput!): ServiceBundle! removeServiceBundle(subscriptionId: ID!, service: ServiceType!): Boolean! addPolicyPack(subscriptionId: ID!, pack: PolicyPackInput!): PolicyPack! removePolicyPack(subscriptionId: ID!, packId: ID!): Boolean! } ``` ### REST Endpoints ``` # Subscription Management POST /api/v1/subscription/subscriptions GET /api/v1/subscription/subscriptions GET /api/v1/subscription/subscriptions/{id} PUT /api/v1/subscription/subscriptions/{id} DELETE /api/v1/subscription/subscriptions/{id} # Quotas GET /api/v1/subscription/subscriptions/{id}/quotas PUT /api/v1/subscription/subscriptions/{id}/quotas GET /api/v1/subscription/subscriptions/{id}/quotas/check # Service Bundles POST /api/v1/subscription/subscriptions/{id}/service-bundles DELETE /api/v1/subscription/subscriptions/{id}/service-bundles/{service} # Policy Packs POST /api/v1/subscription/subscriptions/{id}/policy-packs DELETE /api/v1/subscription/subscriptions/{id}/policy-packs/{packId} ``` ### Example Queries #### Create Subscription ```graphql mutation { createSubscription(input: { name: "production-subscription" tenantId: "tenant-id" type: PRODUCT serviceBundles: [{ service: COMPUTE enabled: true quotas: { vcpu: 100 memory: 512 instances: 50 } }, { service: STORAGE enabled: true quotas: { total: 10000 perInstance: 500 } }] quotas: { compute: { vcpu: 100 memory: 512 instances: 50 } storage: { total: 10000 perInstance: 500 } } policyPacks: [{ name: "security-policy" type: SECURITY policies: [] enforcement: HARD }] }) { id name type serviceBundles { service enabled } } } ``` --- ## IV. Environment Plane API ### GraphQL Schema ```graphql # Environment Management type Environment { id: ID! name: String! type: EnvironmentType! subscription: Subscription! networkIsolation: NetworkIsolation! dataIsolation: DataIsolation! deploymentPolicies: [DeploymentPolicy!]! runtimeSecrets: [Secret!]! complianceOverlays: [ComplianceOverlay!]! region: Region promotionFlow: PromotionFlow createdAt: DateTime! updatedAt: DateTime! metadata: JSON } enum EnvironmentType { DEV INT UAT STAGING PROD REGULATED SOVEREIGN AIR_GAPPED } # Queries type Query { environment(id: ID!): Environment environments(filter: EnvironmentFilter): [Environment!]! promotionFlow(environmentId: ID!): PromotionFlow deploymentPolicies(environmentId: ID!): [DeploymentPolicy!]! } # Mutations type Mutation { createEnvironment(input: CreateEnvironmentInput!): Environment! updateEnvironment(id: ID!, input: UpdateEnvironmentInput!): Environment! deleteEnvironment(id: ID!): Boolean! promoteArtifact(input: PromoteArtifactInput!): PromotionResult! approvePromotion(promotionId: ID!, approved: Boolean!): PromotionResult! configurePromotionFlow(environmentId: ID!, flow: PromotionFlowInput!): PromotionFlow! addDeploymentPolicy(environmentId: ID!, policy: DeploymentPolicyInput!): DeploymentPolicy! removeDeploymentPolicy(environmentId: ID!, policyId: ID!): Boolean! } ``` ### REST Endpoints ``` # Environment Management POST /api/v1/environment/environments GET /api/v1/environment/environments GET /api/v1/environment/environments/{id} PUT /api/v1/environment/environments/{id} DELETE /api/v1/environment/environments/{id} # Promotion POST /api/v1/environment/promotions GET /api/v1/environment/promotions/{id} POST /api/v1/environment/promotions/{id}/approve POST /api/v1/environment/promotions/{id}/reject # Deployment Policies POST /api/v1/environment/environments/{id}/deployment-policies GET /api/v1/environment/environments/{id}/deployment-policies DELETE /api/v1/environment/environments/{id}/deployment-policies/{policyId} ``` ### Example Queries #### Create Environment ```graphql mutation { createEnvironment(input: { name: "production-env" subscriptionId: "subscription-id" type: PROD networkIsolation: { vpcId: "vpc-id" subnetIds: ["subnet-1", "subnet-2"] firewallRules: [] } dataIsolation: { encryptionAtRest: true encryptionInTransit: true accessControls: [] } deploymentPolicies: [{ name: "production-policy" type: POLICY_DRIVEN approvalRequired: true approvers: ["release-manager-1", "release-manager-2"] }] region: "region-a" }) { id name type region { id name } } } ``` #### Promote Artifact ```graphql mutation { promoteArtifact(input: { artifactId: "artifact-id" fromEnvironmentId: "staging-env-id" toEnvironmentId: "prod-env-id" metadata: { version: "1.2.3" changelog: "Production release" } }) { id status approvalRequired approvers } } ``` --- ## V. Content & DevOps Plane API ### GraphQL Schema ```graphql # Content Hierarchy type Enterprise { id: ID! name: String! portfolios: [Portfolio!]! ownership: Ownership! governance: Governance! createdAt: DateTime! updatedAt: DateTime! metadata: JSON } type Portfolio { id: ID! name: String! enterprise: Enterprise! products: [Product!]! ownership: Ownership! governance: Governance! createdAt: DateTime! updatedAt: DateTime! metadata: JSON } type Product { id: ID! name: String! portfolio: Portfolio! applications: [Application!]! ownership: Ownership! governance: Governance! createdAt: DateTime! updatedAt: DateTime! metadata: JSON } type Application { id: ID! name: String! product: Product! components: [Component!]! gitRepos: [GitRepo!]! ownership: Ownership! governance: Governance! createdAt: DateTime! updatedAt: DateTime! metadata: JSON } type Component { id: ID! name: String! application: Application! contentType: ContentType! content: Content! ownership: Ownership! governance: Governance! version: String! lineage: [LineageEntry!]! createdAt: DateTime! updatedAt: DateTime! metadata: JSON } enum ContentType { SOURCE_CODE IAC PIPELINE CONFIG_TEMPLATE DOCUMENTATION DATA_SCHEMA AI_MODEL PROMPT } # Queries type Query { enterprise(id: ID!): Enterprise enterprises: [Enterprise!]! portfolio(id: ID!): Portfolio product(id: ID!): Product application(id: ID!): Application component(id: ID!): Component gitRepo(id: ID!): GitRepo gitRepos(filter: GitRepoFilter): [GitRepo!]! } # Mutations type Mutation { createEnterprise(input: CreateEnterpriseInput!): Enterprise! createPortfolio(input: CreatePortfolioInput!): Portfolio! createProduct(input: CreateProductInput!): Product! createApplication(input: CreateApplicationInput!): Application! createComponent(input: CreateComponentInput!): Component! createGitRepo(input: CreateGitRepoInput!): GitRepo! configureBranchProtection(repoId: ID!, branch: String!, protection: BranchProtectionInput!): BranchProtection! } ``` ### REST Endpoints ``` # Content Hierarchy POST /api/v1/content/enterprises GET /api/v1/content/enterprises GET /api/v1/content/enterprises/{id} POST /api/v1/content/portfolios GET /api/v1/content/portfolios/{id} POST /api/v1/content/products GET /api/v1/content/products/{id} POST /api/v1/content/applications GET /api/v1/content/applications/{id} POST /api/v1/content/components GET /api/v1/content/components/{id} # Git Integration POST /api/v1/content/git-repos GET /api/v1/content/git-repos GET /api/v1/content/git-repos/{id} POST /api/v1/content/git-repos/{id}/branch-protection ``` ### Example Queries #### Create Application ```graphql mutation { createApplication(input: { name: "web-application" productId: "product-id" ownership: { owner: "dev-team" team: "engineering" contact: "dev-team@agency.gov" } governance: { approvalWorkflows: [{ name: "code-review" steps: [{ approver: "tech-lead" role: "TECH_LEAD" }] required: true }] complianceTags: ["HIPAA", "SOC_2"] } }) { id name product { id name } } } ``` #### Create Git Repository ```graphql mutation { createGitRepo(input: { name: "web-application-repo" applicationId: "application-id" url: "https://git.sankofa.nexus/web-application" branchStrategy: { main: "main" develop: "develop" protectedBranches: ["main", "release/*"] } }) { id name url application { id name } } } ``` --- ## VI. Authentication and Authorization ### Authentication All APIs use Keycloak for authentication: ```graphql # Authentication via Keycloak # Token obtained from Keycloak OAuth2/OIDC flow # Include in Authorization header: Bearer ``` ### Authorization Authorization is enforced via RBAC: ```graphql # RBAC roles per plane: # Commercial: Finance Admin, Billing Viewer, Cost Center Owner # Tenancy: Tenant Owner, Security Admin, Identity Admin, Compliance Officer # Subscription: Subscription Owner, Platform Admin, Service Operator, Auditor # Environment: Environment Owner, Release Manager, Operator, Observer # Content: Enterprise Architect, Portfolio Lead, Product Owner, Dev Lead, Contributor, Reviewer, Release Approver ``` ### Example: Authenticated Request ```bash curl -X POST https://api.phoenix.sankofa.nexus/graphql \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "query": "query { tenant(id: \"tenant-id\") { id name } }" }' ``` --- ## VII. Error Handling ### Error Response Format ```json { "errors": [ { "message": "Error message", "extensions": { "code": "ERROR_CODE", "field": "fieldName", "resource": "ResourceType", "resourceId": "resource-id" } } ], "data": null } ``` ### Common Error Codes - `UNAUTHORIZED` - Authentication required - `FORBIDDEN` - Insufficient permissions - `NOT_FOUND` - Resource not found - `VALIDATION_ERROR` - Input validation failed - `QUOTA_EXCEEDED` - Quota limit exceeded - `POLICY_VIOLATION` - Policy violation - `CONFLICT` - Resource conflict - `INTERNAL_ERROR` - Internal server error --- ## VIII. Rate Limiting ### Rate Limits - **GraphQL**: 1000 requests per minute per tenant - **REST**: 500 requests per minute per tenant - **Billing Queries**: 100 requests per minute per client - **Promotion Operations**: 50 requests per minute per environment ### Rate Limit Headers ``` X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 950 X-RateLimit-Reset: 1640995200 ``` --- ## IX. Pagination ### GraphQL Pagination ```graphql type Query { tenants( filter: TenantFilter pagination: PaginationInput ): TenantConnection! } type TenantConnection { edges: [TenantEdge!]! pageInfo: PageInfo! totalCount: Int! } type TenantEdge { node: Tenant! cursor: String! } type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! startCursor: String endCursor: String } input PaginationInput { first: Int after: String last: Int before: String } ``` ### REST Pagination ``` GET /api/v1/tenancy/tenants?page=1&pageSize=50 ``` Response: ```json { "data": [...], "pagination": { "page": 1, "pageSize": 50, "totalPages": 10, "totalCount": 500, "hasNext": true, "hasPrevious": false } } ``` --- ## X. Webhooks and Subscriptions ### GraphQL Subscriptions ```graphql type Subscription { clientUpdated(clientId: ID!): Client! tenantUpdated(tenantId: ID!): Tenant! subscriptionUpdated(subscriptionId: ID!): Subscription! environmentUpdated(environmentId: ID!): Environment! promotionStatusChanged(promotionId: ID!): PromotionResult! quotaExceeded(subscriptionId: ID!): QuotaAlert! } ``` ### REST Webhooks ``` POST /api/v1/webhooks GET /api/v1/webhooks PUT /api/v1/webhooks/{id} DELETE /api/v1/webhooks/{id} ``` Webhook Events: - `client.created` - `client.updated` - `tenant.created` - `tenant.updated` - `subscription.created` - `subscription.updated` - `environment.created` - `environment.updated` - `promotion.started` - `promotion.approved` - `promotion.rejected` - `quota.exceeded` --- ## XI. Multi-Region Support ### Regional API Endpoints ``` # Primary region https://api.phoenix.sankofa.nexus # Regional endpoints https://api-region-a.phoenix.sankofa.nexus https://api-region-b.phoenix.sankofa.nexus https://api-region-c.phoenix.sankofa.nexus ``` ### Cross-Region Operations ```graphql mutation { createCrossRegionSubscription(input: { name: "multi-region-sub" tenantId: "tenant-id" regions: ["region-a", "region-b"] regionalQuotas: { "region-a": { compute: { vcpu: 50, memory: 256 } } "region-b": { compute: { vcpu: 50, memory: 256 } } } }) { id name regions { id name } } } ``` --- ## References - **[Operating Model](./OPERATING_MODEL.md)** - Complete operating model documentation - **[MVP Control Plane](./MVP_CONTROL_PLANE.md)** - MVP API requirements - **[Architecture Diagrams](./OPERATING_MODEL_DIAGRAMS.md)** - API integration diagrams --- **Last Updated**: 2025-01-09 **Version**: 1.0 **Status**: Complete API Specification