openapi: 3.0.3 info: title: Secu Control Plane API version: "0.3.0" description: | Secu is a non-human identity and governance layer for agents. This API is the control plane for provisioning identities (agents/keys), policies, kill-switch, approvals (human-in-the-loop), audit verification, and billing telemetry. servers: - url: https://cp.secuplatform.com components: securitySchemes: SecuAdminToken: type: apiKey in: header name: X-Secu-Admin-Token SecuBearerAuth: type: http scheme: bearer bearerFormat: JWT schemas: Health: type: object properties: status: { type: string } Org: type: object properties: id: { type: string } name: { type: string } created_at: { type: string } stripe_customer_id: { type: string } User: type: object properties: id: { type: string } org_id: { type: string } email: { type: string } role: { type: string } email_verified: { type: boolean } email_verified_at: { type: string } created_at: { type: string } last_login_at: { type: string } Approval: type: object properties: id: { type: string } org_id: { type: string } key: { type: string } agent_id: { type: string } action: { type: string } method: { type: string } path: { type: string } cost_usd: { type: number } policy_id: { type: string } status: { type: string } created_at: { type: string } decided_at: { type: string } expires_at: { type: string } BillingEvent: type: object properties: request_id: { type: string } org_id: { type: string } agent_id: { type: string } key: { type: string } action: { type: string } allow: { type: boolean } status_code: { type: integer } authorized_cost_usd: { type: number } cost_usd: { type: number } fee_usd: { type: number } policy_id: { type: string } deny_reason: { type: string } metering_source: { type: string } timestamp: { type: string } BillingSummary: type: object properties: from: { type: string } to: { type: string } org_id: { type: string } agent_id: { type: string } total_events: { type: integer } allowed_events: { type: integer } denied_events: { type: integer } total_cost_usd: { type: number } total_fee_usd: { type: number } take_rate_bps: { type: number } fee_mode: { type: string } fee_per_action_usd: { type: number } BillingSettings: type: object properties: org_id: { type: string } stripe_customer_id: { type: string } billing_email: { type: string } monthly_retainer_usd: { type: number } stripe_enabled: { type: boolean } currency: { type: string } collection_method: { type: string } days_until_due: { type: integer } StripeInvoice: type: object properties: org_id: { type: string } period_start: { type: string } period_end: { type: string } stripe_invoice_id: { type: string } created_at: { type: string } security: - SecuAdminToken: [] - SecuBearerAuth: [] paths: /healthz: get: summary: Health check responses: "200": description: OK content: application/json: schema: $ref: "#/components/schemas/Health" /v1/auth/login: post: summary: Login (JWT) security: [] requestBody: required: true content: application/json: schema: type: object required: [email, password] properties: email: { type: string } password: { type: string } responses: "200": description: Token issued content: application/json: schema: type: object /v1/auth/signup: post: summary: Signup (self-serve) security: [] requestBody: required: true content: application/json: schema: type: object required: [email, password] properties: org_name: { type: string } email: { type: string } password: { type: string } daily_budget_usd: { type: number } network: { type: string } create_wallet: { type: boolean } stripe_email: { type: string } redirect_url: { type: string } idempotency_key: { type: string } responses: "200": description: Signup result content: application/json: schema: type: object /v1/auth/magic/start: post: summary: Start magic link login security: [] requestBody: required: true content: application/json: schema: type: object required: [email] properties: email: { type: string } redirect_url: { type: string } responses: "200": description: Magic link sent (or token returned when email disabled) content: application/json: schema: type: object /v1/auth/magic/callback: get: summary: Magic link callback (issues JWT, redirects to console) security: [] parameters: - name: token in: query required: true schema: { type: string } responses: "302": description: Redirect to console with JWT fragment "200": description: Token issued (when no redirect URL is set) content: application/json: schema: type: object /v1/auth/verify/start: post: summary: Start email verification (resend) requestBody: required: false content: application/json: schema: type: object properties: redirect_url: { type: string } responses: "200": description: Verification email sent (or token returned when email disabled) content: application/json: schema: type: object /v1/auth/verify/callback: get: summary: Email verification callback (issues JWT, redirects to console) security: [] parameters: - name: token in: query required: true schema: { type: string } responses: "302": description: Redirect to console with JWT fragment "200": description: Verified (token issued) content: application/json: schema: type: object /v1/auth/oidc/enabled: get: summary: OIDC SSO enabled security: [] responses: "200": description: Status content: application/json: schema: type: object /v1/auth/oidc/start: get: summary: Start OIDC SSO flow (redirect) security: [] responses: "302": description: Redirect to IdP /v1/auth/oidc/callback: get: summary: OIDC callback (redirect to console with token) security: [] responses: "302": description: Redirect to console /v1/auth/me: get: summary: Current principal responses: "200": description: Principal content: application/json: schema: type: object /v1/auth/logout: post: summary: Logout (revoke session) responses: "200": description: Logged out content: application/json: schema: type: object /v1/orgs: get: summary: List orgs (scoped by principal) responses: "200": description: Orgs content: application/json: schema: type: object properties: orgs: type: array items: { $ref: "#/components/schemas/Org" } post: summary: Create org (platform admin) requestBody: required: true content: application/json: schema: type: object required: [name] properties: name: { type: string } responses: "200": description: Org created content: application/json: schema: { $ref: "#/components/schemas/Org" } /v1/users: get: summary: List users (scoped by principal) parameters: - name: org_id in: query required: false schema: { type: string } - name: limit in: query required: false schema: { type: integer } responses: "200": description: Users content: application/json: schema: type: object properties: users: type: array items: { $ref: "#/components/schemas/User" } post: summary: Create user (scoped by principal) requestBody: required: true content: application/json: schema: type: object required: [email, password] properties: org_id: { type: string } email: { type: string } password: { type: string } role: { type: string } responses: "200": description: User created content: application/json: schema: { $ref: "#/components/schemas/User" } /v1/state: get: summary: Get redacted state snapshot (scoped by principal) parameters: - name: org_id in: query required: false schema: { type: string } responses: "200": description: State content: application/json: schema: type: object /v1/agents: post: summary: Create agent requestBody: required: true content: application/json: schema: type: object properties: org_id: { type: string } name: { type: string } daily_budget: { type: number } policy_ids: type: array items: { type: string } responses: "200": description: Agent created content: application/json: schema: type: object /v1/policies: post: summary: Create policy requestBody: required: true content: application/json: schema: type: object properties: org_id: { type: string } name: { type: string } allowed_actions: type: array items: { type: string } denied_path_prefixes: type: array items: { type: string } max_cost_per_request: { type: number } require_approval: { type: boolean } approval_min_cost: { type: number } approval_actions: type: array items: { type: string } responses: "200": description: Policy created content: application/json: schema: type: object /v1/vendors: post: summary: Create vendor requestBody: required: true content: application/json: schema: type: object properties: org_id: { type: string } name: { type: string } base_url: { type: string } api_key: { type: string } auth_type: { type: string } header_name: { type: string } responses: "200": description: Vendor created content: application/json: schema: type: object /v1/keys: post: summary: Create Secu key for agent+vendor requestBody: required: true content: application/json: schema: type: object properties: agent_id: { type: string } vendor_id: { type: string } responses: "200": description: Key created content: application/json: schema: type: object /v1/kill-switch/{agent_id}: post: summary: Disable an agent identity parameters: - name: agent_id in: path required: true schema: { type: string } responses: "200": description: Agent disabled content: application/json: schema: type: object /v1/audit/verify: get: summary: Verify audit chain (platform admin) responses: "200": description: Verification result content: application/json: schema: type: object /v1/audit/export.jsonl: get: summary: Export audit log (JSONL) (scoped by principal) parameters: - name: org_id in: query required: false schema: { type: string } responses: "200": description: JSONL export content: application/x-ndjson: schema: type: string /v1/audit/export.csv: get: summary: Export audit log (CSV) (scoped by principal) parameters: - name: org_id in: query required: false schema: { type: string } responses: "200": description: CSV export content: text/csv: schema: type: string /v1/approvals: get: summary: List approvals (scoped by principal) parameters: - name: org_id in: query required: false schema: { type: string } - name: status in: query required: false schema: { type: string } - name: limit in: query required: false schema: { type: integer } responses: "200": description: Approvals content: application/json: schema: type: object properties: approvals: type: array items: $ref: "#/components/schemas/Approval" /v1/approvals/{id}/approve: post: summary: Approve an approval request parameters: - name: id in: path required: true schema: { type: string } responses: "200": description: Approved content: application/json: schema: type: object /v1/approvals/{id}/deny: post: summary: Deny an approval request parameters: - name: id in: path required: true schema: { type: string } responses: "200": description: Denied content: application/json: schema: type: object /v1/billing/summary: get: summary: Usage summary (scoped by principal) parameters: - name: org_id in: query required: false schema: { type: string } - name: from in: query required: false schema: { type: string } - name: to in: query required: false schema: { type: string } - name: agent_id in: query required: false schema: { type: string } responses: "200": description: Summary content: application/json: schema: $ref: "#/components/schemas/BillingSummary" /v1/billing/events: get: summary: Recent billing events (scoped by principal) parameters: - name: org_id in: query required: false schema: { type: string } - name: limit in: query required: false schema: { type: integer } responses: "200": description: Events content: application/json: schema: type: object properties: events: type: array items: $ref: "#/components/schemas/BillingEvent" /v1/billing/settings: get: summary: Billing settings (Stripe) (scoped by principal) parameters: - name: org_id in: query required: false schema: { type: string } responses: "200": description: Settings content: application/json: schema: $ref: "#/components/schemas/BillingSettings" post: summary: Update billing settings (Stripe) (scoped by principal) requestBody: required: true content: application/json: schema: type: object required: [org_id, stripe_customer_id] properties: org_id: { type: string } stripe_customer_id: { type: string } billing_email: { type: string } monthly_retainer_usd: { type: number } responses: "200": description: Updated content: application/json: schema: $ref: "#/components/schemas/BillingSettings" /v1/billing/stripe/customer: post: summary: Create Stripe customer and attach to org (scoped by principal) requestBody: required: true content: application/json: schema: type: object required: [org_id] properties: org_id: { type: string } name: { type: string } email: { type: string } responses: "200": description: Customer created or already set content: application/json: schema: type: object /v1/billing/stripe/invoice: post: summary: Create Stripe invoice for Secu fees (scoped by principal) requestBody: required: true content: application/json: schema: type: object required: [org_id] properties: org_id: { type: string } from: { type: string } to: { type: string } collection_method: { type: string } days_until_due: { type: integer } responses: "200": description: Invoice created / already exists content: application/json: schema: type: object /v1/billing/stripe/invoices: get: summary: List Stripe invoices created by Secu (scoped by principal) parameters: - name: org_id in: query required: false schema: { type: string } - name: limit in: query required: false schema: { type: integer } responses: "200": description: Invoices content: application/json: schema: type: object properties: invoices: type: array items: $ref: "#/components/schemas/StripeInvoice" /v1/public/tx/stats: get: summary: Public network stats (last 30 days by default) security: [] parameters: - name: from in: query required: false schema: { type: string } - name: to in: query required: false schema: { type: string } responses: "200": description: Public stats content: application/json: schema: type: object /v1/tx/stats: get: summary: Transaction stats (scoped by principal) parameters: - name: org_id in: query required: false schema: { type: string } - name: from in: query required: false schema: { type: string } - name: to in: query required: false schema: { type: string } responses: "200": description: Stats content: application/json: schema: type: object /v1/marketplace/hire: post: summary: Hire an agent from the marketplace (creates a linked key and optional initial charge) requestBody: required: true content: application/json: schema: type: object required: [to_agent_id] properties: org_id: { type: string } to_agent_id: { type: string } pricing_model: { type: string } rail: { type: string } currency: { type: string } amount: { type: integer } amount_usd: { type: number } network: { type: string } asset: { type: string } daily_budget_usd: { type: number } charge_now: { type: boolean } idempotency_key: { type: string } responses: "200": description: Hire result content: application/json: schema: type: object /v1/tx/transfer: post: summary: Transfer funds between agents description: | This endpoint is called with an agent key in `Authorization: Bearer `. (Not a console JWT.) security: [] requestBody: required: true content: application/json: schema: type: object required: [rail, currency, amount] properties: to_agent_id: { type: string } to_address: { type: string } rail: { type: string } currency: { type: string } amount: { type: integer } amount_usd: { type: number } network: { type: string } asset: { type: string } mandate_id: { type: string } shared_payment_token: { type: string } idempotency_key: { type: string } metadata: type: object additionalProperties: { type: string } responses: "200": description: Transfer result content: application/json: schema: type: object