components: schemas: Activity: properties: FieldCount: additionalProperties: type: integer type: object date: type: string type: object App: properties: basicConfigOptions: items: $ref: '#/components/schemas/ApplicationConfigOption' type: array createdTime: type: string description: type: string details: $ref: '#/components/schemas/ApplicationView' displayName: type: string manifest: type: string name: type: string namespace: type: string owner: type: string parameters: type: string status: type: string template: type: string updatedTime: type: string url: type: string type: object ApplicationConfigOption: properties: parameter: type: string setting: type: string type: object ApplicationEvent: properties: count: type: integer firstTime: type: string involvedObject: type: string lastTime: type: string message: type: string name: type: string reason: type: string source: type: string type: type: string type: object ApplicationView: properties: createdTime: type: string credentials: items: $ref: '#/components/schemas/EnvVariable' type: array deployments: items: $ref: '#/components/schemas/DeploymentDetail' type: array events: items: $ref: '#/components/schemas/ApplicationEvent' type: array metrics: $ref: '#/components/schemas/ResourceMetrics' namespace: type: string services: items: $ref: '#/components/schemas/ServiceDetail' type: array status: type: string type: object Approval: properties: clause: type: string reason: type: string status: type: string type: object Article: properties: content: items: $ref: '#/components/schemas/Block' type: array createdTime: type: string displayName: type: string glossary: items: type: string type: array name: type: string owner: type: string text: type: string type: type: string workflow: type: string type: object Asset: properties: createdTime: type: string displayName: type: string id: type: string name: type: string owner: type: string password: type: string properties: type: string provider: type: string region: type: string state: type: string tag: type: string type: type: string updatedTime: type: string username: type: string zone: type: string type: object Audit: properties: action: type: string block: type: string block2: type: string blockHash: type: string blockHash2: type: string city: type: string clientIp: type: string count: type: integer createdTime: type: string errorText: type: string id: type: integer isTriggered: type: boolean language: type: string method: type: string name: type: string needCommit: type: boolean object: type: string organization: type: string owner: type: string provider: type: string provider2: type: string query: type: string region: type: string requestUri: type: string response: type: string section: type: string transaction: type: string transaction2: type: string unit: type: string user: type: string userAgent: type: string type: object BankTally: properties: ingested: type: integer posted: type: integer questions: type: integer reconciled: type: integer skipped: type: integer transfers: type: integer type: object Block: properties: prompt: type: string state: type: string text: type: string textEn: type: string type: type: string type: object Candidate: properties: isHit: type: boolean text: type: string type: object Capability: properties: description: type: string href: type: string name: type: string stage: type: string type: object CaptureBatch: properties: batch: items: $ref: '#/components/schemas/CaptureEvent' type: array events: items: $ref: '#/components/schemas/CaptureEvent' type: array type: object CaptureEvent: properties: anonymousId: type: string channel: type: string clip: $ref: '#/components/schemas/ClipBody' currency: type: string distinctId: type: string environment: type: string error: $ref: '#/components/schemas/Exception' event: type: string groupId: type: string groupType: type: string kind: type: string level: type: string library: type: string libraryVersion: type: string log: $ref: '#/components/schemas/LogBody' messageId: type: string metric: $ref: '#/components/schemas/MetricBody' path: type: string personId: type: string product: type: string productId: type: string properties: additionalProperties: {} type: object quantity: type: integer refCode: type: string referrer: type: string release: type: string resource: type: string revenue: type: number service: type: string sessionId: type: string signupWeek: type: string site: type: string span: $ref: '#/components/schemas/SpanBody' spanId: type: string timestamp: type: string traceId: type: string type: type: string url: type: string utm: $ref: '#/components/schemas/UTM' type: object CaptureResult: properties: accepted: type: integer dropped: type: integer type: object Cart: properties: createdAt: description: CreatedAt is when the cart was opened, RFC3339. type: string currency: description: Currency is the ISO 4217 code every amount below is denominated in. type: string discountCents: description: DiscountCents is what coupons and promotions took off, in whole cents. format: int64 type: integer email: description: Email is the shopper's address, when the cart carries one. type: string id: description: "ID is the cart's id \u2014 what every other cart op addresses it by, and what a\nstorefront persists\ \ against the browser session." type: string items: description: Items are the cart's lines, in the order they were added. items: $ref: '#/components/schemas/CartItem' type: array lineTotalCents: description: LineTotalCents is the sum of the lines before any discount, in whole cents. format: int64 type: integer order: description: 'Order is the order this cart became, once checkout completed it. Empty until then, and its presence is what makes a cart final.' type: string shippingCents: description: 'ShippingCents is the shipping charge, in whole cents. It stays zero until a shipping option is priced at checkout.' format: int64 type: integer status: description: 'Status is "active" for a cart still being filled, "ordered" once checkout turned it into an order, and "discarded" when the shopper abandoned it.' type: string store: description: Store is the storefront the cart is being filled on. type: string subtotalCents: description: SubtotalCents is LineTotalCents less DiscountCents, in whole cents. format: int64 type: integer taxCents: description: 'TaxCents is the sales tax, in whole cents. It stays zero until checkout resolves the shopper''s tax region.' format: int64 type: integer totalCents: description: 'TotalCents is what the shopper pays: subtotal plus shipping plus tax, in whole cents.' format: int64 type: integer updatedAt: description: UpdatedAt is when the cart was last amended, RFC3339. type: string user: description: User is the signed-in shopper this cart belongs to, empty for a guest cart. type: string type: object CartItem: properties: free: description: 'Free reports a line that costs nothing because a coupon or a promotion made it so, rather than because its price is zero.' type: boolean id: description: "ID is the line's identity \u2014 the variant id when the line is a variant,\notherwise the product\ \ id. It is what a subsequent set call addresses." type: string kind: description: 'Kind is "variant" when this line is a specific sellable variant and "product" when it is the product itself.' type: string name: description: 'Name is the item''s display name, cached onto the line when it was added so a cart renders without a second read.' type: string priceCents: description: 'PriceCents is the unit price in whole cents, cached at the moment the line was added. The line''s contribution to the cart is this times Quantity.' format: int64 type: integer quantity: description: Quantity is how many units of this item the cart holds. format: int64 type: integer sku: description: "SKU is the line's stock-keeping unit \u2014 the variant's when it has one,\notherwise the product's.\ \ Empty when neither carries one." type: string type: object CartItemSet: properties: id: description: ID is the cart to amend, from the path. type: string product: description: 'Product names the catalog product to set, by its id or its URL slug. Give this or Variant, never both; a request naming neither is refused.' type: string quantity: description: "Quantity is how many of that item the cart should hold AFTER this call \u2014 it\nis the resulting\ \ count, not a delta, so sending 3 twice leaves 3 and not 6.\nZERO REMOVES the line, which is the only way to\ \ take an item out." format: int64 type: integer variant: description: "Variant names the specific sellable variant to set, by its id or its SKU.\nPrefer it over Product\ \ for anything sold in sizes, colours or tiers \u2014 the\nprice and the stock are the variant's, not the product's." type: string type: object CartOpen: properties: currency: description: 'Currency is the ISO 4217 code the cart is priced in, lower-cased. Empty means usd.' type: string email: description: 'Email is the shopper''s address, for a cart that belongs to someone who has not signed in. It is what a guest checkout and an abandoned-cart follow-up key on. Empty is fine.' type: string store: description: 'Store is the storefront this cart is being filled on. Empty uses the org''s default store, which is what a single-storefront merchant always wants.' type: string user: description: 'User is the id of the signed-in shopper this cart belongs to, when there is one. Empty means a guest cart identified only by its own id.' type: string type: object Chat: properties: category: type: string clientIp: type: string clientIpDesc: type: string createdTime: type: string currency: type: string displayName: type: string isDeleted: type: boolean isHidden: type: boolean messageCount: type: integer modelProvider: type: string name: type: string needTitle: type: boolean organization: type: string owner: type: string price: type: number store: type: string tokenCount: type: integer type: type: string updatedTime: type: string user: type: string user1: type: string user2: type: string userAgent: type: string userAgentDesc: type: string users: items: type: string type: array type: object ClipBody: properties: bytes: type: integer duration: type: integer object: type: string type: object Connection: properties: clientIp: type: string clientIpDesc: type: string code: type: integer commandCount: type: integer connectionId: type: string createdTime: type: string creator: type: string endTime: type: string height: type: integer message: type: string mode: type: string name: type: string node: type: string operations: items: type: string type: array owner: type: string protocol: type: string recording: type: string reviewed: type: boolean startTime: type: string status: type: string userAgent: type: string userAgentDesc: type: string width: type: integer type: object ContainerDetail: properties: image: type: string name: type: string resources: $ref: '#/components/schemas/ResourceRequests' type: object Count: properties: chatCount: type: integer currency: type: string date: type: string messageCount: type: integer price: type: number tokenCount: type: integer userCount: type: integer type: object Declaration: properties: application: type: string automated: type: boolean component: type: string digest: type: string env: items: $ref: '#/components/schemas/declareEnv' type: array hosts: items: type: string type: array name: type: string org: type: string partOf: type: string path: type: string project: type: string replicas: type: integer repository: type: string secrets: items: $ref: '#/components/schemas/SecretRef' type: array tag: type: string type: object DeploymentDetail: properties: containers: items: $ref: '#/components/schemas/ContainerDetail' type: array createdTime: type: string name: type: string readyReplicas: type: integer replicas: type: integer status: type: string type: object DestinationField: properties: example: type: string key: type: string label: type: string required: type: boolean type: object DestinationStatus: properties: account: type: string category: type: string config: additionalProperties: type: string type: object connected: type: boolean enabled: type: boolean fields: items: $ref: '#/components/schemas/DestinationField' type: array live: type: boolean name: type: string pixel: type: boolean platform: type: string secrets: items: type: string type: array type: object DocSearchResult: properties: breadcrumbs: items: type: string type: array content: type: string file_id: type: string id: type: string title: type: string type: type: string url: type: string type: object EnvVariable: properties: name: type: string value: type: string type: object Envelope: description: 'The resource surface''s response. `status` is the verdict, not the HTTP code: a handled failure is still 200.' properties: data: description: The operation's own result. data2: description: A second result the operation defines, when it has one. msg: description: Empty on success, the reason on failure. type: string status: enum: - ok - error type: string required: - status - msg type: object ExampleQuestion: properties: image: type: string text: type: string title: type: string type: object Exception: properties: frames: items: $ref: '#/components/schemas/Frame' type: array handled: type: boolean message: type: string stack: type: string type: type: string type: object Extracted: properties: category: type: string currency: type: string issuedAt: type: string lineItems: items: $ref: '#/components/schemas/LineItem' type: array merchant: type: string note: type: string taxCents: type: integer totalCents: type: integer type: object File: properties: createdTime: type: string errorText: type: string filename: type: string name: type: string owner: type: string size: type: integer status: type: string storageProvider: type: string store: type: string tokenCount: type: integer url: type: string type: object FinetuneJob: properties: baseModel: type: string costCents: type: integer crName: type: string createdBy: type: string createdTime: type: string dataset: type: string deployUrl: type: string deployedModel: type: string displayName: type: string error: type: string finishedTime: type: string gpuCount: type: integer gpuSeconds: type: integer gpuType: type: string hyperparams: type: string message: type: string metered: type: boolean method: type: string name: type: string namespace: type: string numNodes: type: integer outputUri: type: string owner: type: string preset: type: string progress: type: integer runtime: type: string startedTime: type: string status: type: string task: type: string updatedTime: type: string type: object FormItem: properties: label: type: string name: type: string type: type: string visible: type: boolean width: type: string type: object Frame: properties: column: type: integer file: type: string function: type: string line: type: integer type: object Graph: properties: category: type: string createdTime: type: string density: type: integer displayName: type: string endTime: type: string errorText: type: string layout: type: string name: type: string owner: type: string startTime: type: string store: type: string text: type: string type: object HfDataset: properties: downloads: type: integer gated: {} id: type: string lastModified: type: string likes: type: integer private: type: boolean tags: items: type: string type: array type: object HfModel: properties: downloads: type: integer gated: {} id: type: string lastModified: type: string library_name: type: string likes: type: integer pipeline_tag: type: string private: type: boolean tags: items: type: string type: array type: object HfRepoInfo: properties: downloads: type: integer gated: {} id: type: string lastModified: type: string likes: type: integer pipeline_tag: type: string private: type: boolean siblings: items: $ref: '#/components/schemas/HfSibling' type: array tags: items: type: string type: array type: object HfSibling: properties: rfilename: type: string size: type: integer type: object InboxItem: properties: category: type: string confidence: type: string createdAt: type: string extracted: $ref: '#/components/schemas/Extracted' filename: type: string id: type: string status: type: string vendor: type: string type: object Index: properties: _links: additionalProperties: $ref: '#/components/schemas/Link' type: object description: type: string name: type: string operations: items: $ref: '#/components/schemas/Op' type: array stage: type: string type: object IngestStats: properties: async: type: boolean documentsIndexed: type: integer errors: items: type: string type: array filesIngested: type: integer filesSkipped: type: integer indexName: type: string skipped: items: type: string type: array source: type: string store: type: string workflowId: type: string type: object Label: properties: endTime: type: number id: type: string speaker: type: string startTime: type: number tag1: type: string tag2: type: string tag3: type: string text: type: string type: type: string user: type: string type: object Layout: properties: category: type: string createdTime: type: string displayName: type: string formItems: items: $ref: '#/components/schemas/FormItem' type: array name: type: string owner: type: string position: type: string tag: type: string type: type: string url: type: string type: object Leg: properties: account: type: string credit: type: integer debit: type: integer type: object LineItem: properties: amountCents: type: integer description: type: string type: object Link: properties: href: type: string type: object LogBody: properties: body: type: string number: type: integer severity: type: string type: object MCPError: properties: code: type: integer data: {} message: type: string type: object MCPRequest: properties: id: {} jsonrpc: type: string method: type: string params: additionalProperties: {} type: object type: object MCPResponse: properties: error: $ref: '#/components/schemas/MCPError' id: {} jsonrpc: type: string result: additionalProperties: {} type: object type: object Memory: properties: content: type: string createdTime: type: string dimension: type: integer embedding: items: type: number type: array kind: type: string metadata: additionalProperties: type: string type: object name: type: string owner: type: string score: type: number updatedTime: type: string userId: type: string type: object Message: properties: answeredTime: type: string author: type: string chat: type: string claimedTime: type: string comment: type: string createdTime: type: string currency: type: string dislikeUsers: items: type: string type: array embeddingProvider: type: string errorText: type: string fileName: type: string isAlerted: type: boolean isDeleted: type: boolean isHidden: type: boolean isRegenerated: type: boolean likeUsers: items: type: string type: array modelProvider: type: string name: type: string needNotify: type: boolean organization: type: string owner: type: string price: type: number reasonText: type: string replyTo: type: string searchResults: items: $ref: '#/components/schemas/model.SearchResult' type: array store: type: string suggestions: items: $ref: '#/components/schemas/Candidate' type: array text: type: string textTokenCount: type: integer tokenCount: type: integer toolCalls: items: $ref: '#/components/schemas/model.ToolCall' type: array transactionId: type: string user: type: string vectorScores: items: $ref: '#/components/schemas/VectorScore' type: array webSearchEnabled: type: boolean type: object MetricBody: properties: labels: additionalProperties: {} type: object name: type: string value: type: number type: object ModelAccess: properties: createdTime: type: string email: type: string model: type: string owner: type: string status: type: string updatedTime: type: string user: type: string type: object ModelRoute: properties: costInPerMillion: type: number costOutPerMillion: type: number createdTime: type: string enabled: type: boolean fallback1Provider: type: string fallback1Upstream: type: string fallback2Provider: type: string fallback2Upstream: type: string hidden: type: boolean inputPricePerMillion: type: number modelName: type: string outputPricePerMillion: type: number ownedBy: type: string owner: type: string premium: type: boolean priced: type: boolean provider: type: string updatedTime: type: string upstream: type: string type: object Node: properties: autoQuery: type: boolean category: type: string cpuSize: type: string createdTime: type: string description: type: string displayName: type: string enableRemoteApp: type: boolean isPermanent: type: boolean language: type: string machineName: type: string memSize: type: string name: type: string os: type: string owner: type: string patches: items: $ref: '#/components/schemas/Patch' type: array privateIp: type: string publicIp: type: string remoteApps: items: $ref: '#/components/schemas/RemoteApp' type: array remotePassword: type: string remotePort: type: integer remoteProtocol: type: string remoteUsername: type: string services: items: $ref: '#/components/schemas/Service' type: array size: type: string tag: type: string type: type: string updatedTime: type: string type: object Op: properties: href: type: string method: type: string operationId: type: string summary: type: string type: object PagesDeploy: properties: branch: type: string type: object Patch: properties: category: type: string expectedStatus: type: string installTime: type: string message: type: string name: type: string size: type: string status: type: string title: type: string url: type: string type: object Properties: properties: collectedTime: type: string subject: type: string type: object Provider: properties: apiVersion: type: string asset: type: string browserUrl: type: string category: type: string chain: type: string clientId: type: string clientSecret: type: string compatibleProvider: type: string configText: type: string contractMethod: type: string contractName: type: string createdTime: type: string currency: type: string displayName: type: string enableThinking: type: boolean errorText: type: string flavor: type: string frequencyPenalty: type: number inputPricePerThousandTokens: type: number isDefault: type: boolean isRemote: type: boolean mcpTools: items: $ref: '#/components/schemas/agent.McpTools' type: array name: type: string network: type: string outputPricePerThousandTokens: type: number owner: type: string presencePenalty: type: number providerKey: type: string providerUrl: type: string rawText: type: string region: type: string resultSummary: type: string runner: type: string signCert: type: string signKey: type: string state: type: string subType: type: string target: type: string targetMode: type: string temperature: type: number testContent: type: string text: type: string topK: type: integer topP: type: number type: type: string userCert: type: string userKey: type: string type: object Question: properties: account: type: string amount: type: string id: type: string kind: type: string postedAt: type: string text: type: string type: object RagEmbedResult: properties: chunks: type: integer file_id: type: string filename: type: string index_name: type: string store: type: string type: object Remark: properties: isPublic: type: boolean score: type: string text: type: string timestamp: type: string user: type: string type: object RemoteApp: properties: 'no': type: integer remoteAppArgs: type: string remoteAppDir: type: string remoteAppName: type: string type: object ResourceMetrics: properties: cpuPercentage: type: number cpuUsage: type: string memoryPercentage: type: number memoryUsage: type: string podCount: type: integer type: object ResourceRequests: properties: cpu: type: string memory: type: string type: object Root: properties: _links: additionalProperties: $ref: '#/components/schemas/Link' type: object capabilities: items: $ref: '#/components/schemas/Capability' type: array type: object Scale: properties: createdTime: type: string displayName: type: string name: type: string owner: type: string state: type: string text: type: string type: object ScanDraft: properties: balanced: type: boolean category: type: string confidence: type: string extracted: $ref: '#/components/schemas/Extracted' questions: items: $ref: '#/components/schemas/Question' type: array scanId: type: string vendor: type: string voucher: $ref: '#/components/schemas/Voucher' type: object SecretRef: properties: env: type: string keys: items: type: string type: array path: type: string project: type: string type: object Service: properties: expectedStatus: type: string message: type: string name: type: string 'no': type: integer path: type: string port: type: integer processId: type: integer status: type: string subStatus: type: string type: object ServiceDetail: properties: clusterIP: type: string createdTime: type: string externalHost: type: string externalIP: type: string internalHost: type: string name: type: string ports: items: $ref: '#/components/schemas/ServicePort' type: array type: type: string type: object ServicePort: properties: name: type: string nodePort: type: integer port: type: integer protocol: type: string url: type: string type: object Session: properties: createdTime: type: string name: type: string owner: type: string sessionId: items: type: string type: array type: object SpanBody: properties: duration: type: integer id: type: string kind: type: string parent: type: string status: type: string trace: type: string type: object Store: properties: agentProvider: type: string avatar: type: string builtinTools: items: type: string type: array chatCount: type: integer childModelProviders: items: type: string type: array childStores: items: type: string type: array createdTime: type: string disableFileUpload: type: boolean displayName: type: string embeddingProvider: type: string enableTtsStreaming: type: boolean exampleQuestions: items: $ref: '#/components/schemas/ExampleQuestion' type: array faviconUrl: type: string fileTree: $ref: '#/components/schemas/TreeFile' footerHtml: type: string forbiddenWords: items: type: string type: array frequency: type: integer hideThinking: type: boolean htmlTitle: type: string imageProvider: type: string isDefault: type: boolean knowledgeCount: type: integer limitMinutes: type: integer logoUrl: type: string memoryLimit: type: integer messageCount: type: integer modelProvider: type: string name: type: string navItems: items: type: string type: array owner: type: string prompt: type: string propertiesMap: additionalProperties: $ref: '#/components/schemas/Properties' type: object searchProvider: type: string showAutoRead: type: boolean speechToTextProvider: type: string splitProvider: type: string state: type: string storageProvider: type: string storageSubpath: type: string suggestionCount: type: integer textToSpeechProvider: type: string themeColor: type: string title: type: string vectorStoreId: type: string vectorStores: items: type: string type: array welcome: type: string welcomeText: type: string welcomeTitle: type: string type: object Sweep: properties: asset: type: string command: type: string createdTime: type: string displayName: type: string errorText: type: string name: type: string owner: type: string provider: type: string rawResult: type: string result: type: string resultSummary: type: string runner: type: string state: type: string target: type: string targetMode: type: string updatedTime: type: string type: object Task: properties: activity: type: string createdTime: type: string displayName: type: string documentText: type: string documentUrl: type: string example: type: string grade: type: string labels: items: type: string type: array log: type: string name: type: string owner: type: string path: type: string provider: type: string result: type: string scale: type: string score: type: number subject: type: string topic: type: string type: type: string type: object Template: properties: basicConfigOptions: items: $ref: '#/components/schemas/TemplateConfigOption' type: array createdTime: type: string description: type: string displayName: type: string enableBasicConfig: type: boolean icon: type: string manifest: type: string name: type: string owner: type: string readme: type: string updatedTime: type: string version: type: string type: object TemplateConfigOption: properties: default: type: string description: type: string options: items: type: string type: array parameter: type: string required: type: boolean type: type: string type: object TrafficCountryCount: properties: count: type: integer country: type: string type: object TrafficGlobe: properties: points: items: $ref: '#/components/schemas/TrafficPoint' type: array totals: $ref: '#/components/schemas/TrafficTotals' window: $ref: '#/components/schemas/TrafficWindow' type: object TrafficPoint: properties: byService: additionalProperties: type: integer type: object byTask: additionalProperties: type: integer type: object count: type: integer country: type: string lat: type: number lon: type: number region: type: string type: object TrafficTotals: properties: rpm_60m: type: number rps_1m: type: number top_countries: items: $ref: '#/components/schemas/TrafficCountryCount' type: array type: object TrafficWindow: properties: minutes: type: integer since: type: string until: type: string type: object TreeFile: properties: children: items: $ref: '#/components/schemas/TreeFile' type: array createdTime: type: string isLeaf: type: boolean key: type: string size: type: integer title: type: string url: type: string type: object UTM: properties: campaign: type: string content: type: string medium: type: string source: type: string term: type: string type: object Vector: properties: createdTime: type: string currency: type: string data: items: type: number type: array dimension: type: integer displayName: type: string file: type: string index: type: integer name: type: string owner: type: string price: type: number provider: type: string score: type: number store: type: string text: type: string tokenCount: type: integer type: object VectorScore: properties: score: type: number vector: type: string type: object Video: properties: audioUrl: type: string class: type: string coverUrl: type: string createdTime: type: string dataUrl: type: string dataUrls: items: type: string type: array description: type: string displayName: type: string downloadUrl: type: string editMode: type: string excellentCount: type: integer grade: type: string grade2: type: string isPublic: type: boolean keywords: items: type: string type: array labelCount: type: integer labels: items: $ref: '#/components/schemas/Label' type: array lesson: type: string name: type: string owner: type: string playAuth: type: string remarks: items: $ref: '#/components/schemas/Remark' type: array remarks2: items: $ref: '#/components/schemas/Remark' type: array reviewState: type: string school: type: string segmentCount: type: integer segments: items: $ref: '#/components/schemas/Label' type: array stage: type: string state: type: string subject: type: string tag: type: string tagOnPause: type: boolean task1: type: string task2: type: string task3: type: string template: type: string topic: type: string type: type: string unit: type: string videoId: type: string videoLength: type: string wordCountMap: additionalProperties: type: integer type: object type: object Voucher: properties: description: type: string legs: items: $ref: '#/components/schemas/Leg' type: array postingAt: type: string sourceId: type: string sourceKind: type: string type: object Workflow: properties: createdTime: type: string displayName: type: string message: type: string name: type: string owner: type: string questionTemplate: type: string text: type: string text2: type: string type: object account.apiKey: properties: createdAt: description: CreatedAt is when the key last changed, as IAM records it. type: string key: description: 'Key is the FULL value, and is present for a publishable key only: it is public by construction and useless to its holder if it cannot be read back.' type: string limit: description: "Limit is what this key may reach, as `kind:name` entries \u2014 `model:zen5`,\n`project:acme`, `product:commerce`.\ \ Absent means the key reaches whatever\nits holder does, which is what every key minted before limits existed\ \ does\nand must keep doing." items: type: string type: array prefix: description: "Prefix is the recognizable, non-secret head of the key \u2014 enough to tell two\nkeys apart, never\ \ enough to use one." type: string type: description: 'Type is the key class: secret (sk-) or publishable (pk-).' type: string type: object account.apiKeyList: properties: keys: description: Keys is every key the caller holds, at most one per type. items: $ref: '#/components/schemas/account.apiKey' type: array type: object account.appearance: properties: accent: description: "Accent is the one hue \u2014 a CSS colour token (a hex, or a bounded functional\ncolour like rgb()/oklch()).\ \ Anything else is dropped rather than stored." type: string density: description: 'Density is the spacing step: "compact", "default" or "comfortable".' type: string type: description: 'Type is the text-size multiplier, clamped to the ramp window [0.85, 1.4]. Absent (0) leaves the published default.' format: double type: number type: object account.csrfResp: properties: csrfToken: description: 'Token is the value to send back in the X-CSRF-Token header. It is bound to the caller''s identity, so it authorizes changes as them and as nobody else.' type: string expiresIn: description: 'ExpiresIn is the token''s lifetime in seconds. Fetch a new one when it lapses; a change with an expired token is refused.' format: int64 type: integer type: object account.embedStatusResp: properties: app: description: App is the app this verdict is about. type: string embedUrl: description: "EmbedURL is the in-app landing URL to frame. Empty when the caller is not\nentitled \u2014 a non-entitled\ \ caller never receives it." type: string entitled: description: Entitled is whether the caller's org may frame this brand-owned app. type: boolean origin: description: Origin is the app's origin on this deployment's own brand domain. type: string phase: description: 'Phase is the verdict in one word: not-entitled, not-provisioned or ready.' type: string reachable: description: Reachable is whether the app answered the liveness probe. type: boolean type: object account.keyTypeIn: properties: limit: description: "Limit narrows what the minted key may reach, as `kind:name` entries:\n`model:zen5`, `project:acme`,\ \ `product:commerce`, or `model:*` for a whole\nkind. It only ever NARROWS \u2014 a key can never reach further\ \ than the person\nwho minted it \u2014 so an unrecognised kind costs availability, never privilege.\n\nOmitted\ \ mints an unrestricted key, because that is what every key in the\nestate is today and a default that restricted\ \ would revoke all of them.\n\nExample: {\"type\": \"secret\", \"limit\": [\"model:zen5\", \"project:acme\"]}" items: type: string type: array type: description: 'Type is the key class to act on: "secret" (sk-, session-equivalent, belongs on a server) or "publishable" (pk-, org-identifying, safe in a browser bundle). Omitted means secret, which is what every existing caller means.' type: string type: object account.mintedKey: properties: accessKey: description: 'AccessKey is the same value under its predecessor name, carried so callers written against the older field keep working. One value, two names.' type: string key: description: "Key is the credential, returned ONCE \u2014 a secret key is unreadable afterwards." type: string limit: description: 'Limit is what the minted key may reach, echoed back so the caller can see the narrowing took. Absent means unrestricted.' items: type: string type: array type: description: Type is the class of key that was minted. type: string type: object account.onboardReq: properties: name: description: 'Name is the organization''s display name. Ignored when personal is true, which derives the name from the caller''s own username instead.' type: string personal: description: "Personal asks for the caller's own space: the name is derived from their\nusername and the slug auto-suffixes\ \ to stay unique. Meaningless \u2014 and refused\n\u2014 for a caller who already has an organization." type: boolean type: object account.onboardResp: properties: accessKey: description: 'AccessKey is the identifier of the org-scoped credential provisioning minted with the organization. Present on a first run that actually minted one.' type: string accessSecret: description: "AccessSecret is that credential's confidential half, returned ONCE \u2014 on the\nresponse that mints\ \ it and never again. IAM keeps only its argon2id digest\nand blanks the plaintext, so this is the single moment\ \ it exists in a form\nits owner can read; a replay of the same provision re-reveals nothing." type: string additional: description: "Additional is true when the caller already had an organization and this one\nwas created WITHOUT moving\ \ them into it \u2014 they reach it via the org switcher." type: boolean displayName: description: DisplayName is the organization's human name. type: string org: description: Org is the created organization's slug, which is what X-Org-Id carries. type: string type: object account.revokedKey: properties: ok: description: 'OK is true when the key was revoked. A failure is an error status, never a false here.' type: boolean type: description: "Type is the key class that was revoked, resolved \u2014 so a caller that named\nnothing can see it\ \ revoked the secret key." type: string type: object ad.AdCampaign: properties: account: description: 'Account is the provider ad-account the campaign runs under, in Meta''s act_ form. Empty until the org supplies one or a launch resolves it.' type: string budget: description: 'Budget is the campaign''s authorized spend in MINOR units (cents). Negative clamps to 0. It is the org''s stored plan: a Meta launch creates the campaign object only, and the delivering budget lives on the ad set.' format: int64 type: integer createdAt: description: 'CreatedAt is when the campaign was first stored, in unix seconds. It never changes, including across a full-replace update.' format: int64 type: integer externalId: description: "ExternalID is the ad network's own campaign id, written by a successful\nlaunch and by nothing else\ \ \u2014 an update never touches it. Empty means this\ncampaign has never reached its network." type: string id: description: 'ID is the campaign''s server-minted handle, "camp_" + 32 hex. A create body cannot choose it, and it is the id every other route addresses.' type: string name: description: 'Name is the campaign''s display label, and the name Meta creates the campaign object under at launch. Required; trimmed and bounded to 1024 bytes.' type: string objective: description: "Objective is the campaign goal spelled as the provider names it\n(\"conversions\", \"OUTCOME_TRAFFIC\"\ ), passed through to the network verbatim at\nlaunch \u2014 Meta defaults an empty one to OUTCOME_TRAFFIC. Free\ \ text, bounded to\n1024 bytes; no vocabulary is enforced here." type: string platform: description: "Platform is the ad network: meta, google, tiktok or x, and nothing else \u2014 a\nwrite naming another\ \ is 400. Empty stores as meta. Only meta executes today;\nlaunching any of the other three is 501." type: string spend: description: "Spend is spend-to-date in MINOR units (cents), as last written through create\nor update. Negative\ \ clamps to 0. It is NOT read back from the network \u2014 that\nis a separate insights call \u2014 so 0 means\ \ nothing was recorded here, not that\nnothing was spent." format: int64 type: integer status: description: "Status is the lifecycle: draft, active, paused or completed, and nothing else\n\u2014 a write naming\ \ another is 400. Empty stores as draft; a successful launch\nsets active. It records what this deployment did,\ \ not what the ad network\ncurrently reports." type: string updatedAt: description: "UpdatedAt is when the row was last written, in unix seconds \u2014 set by create,\nupdate and launch.\ \ Listings are ordered by it, newest first." format: int64 type: integer type: object ad.adSummary: properties: active: description: Active is how many of those campaigns are in the active state. format: int64 type: integer budget: description: Budget is the summed budget of every campaign in the org, in cents. format: int64 type: integer campaigns: description: Campaigns is how many campaigns the org has, in every state. format: int64 type: integer spend: description: Spend is the summed spend of every campaign in the org, in cents. format: int64 type: integer type: object ad.campaignInput: properties: account: description: Account is the provider ad-account this campaign runs on (Meta act_). Optional. type: string budget: description: Budget is the campaign budget in MINOR units (cents). Negative values clamp to 0. format: int64 type: integer name: description: Name is the campaign's display label. Required; trimmed and bounded to 1024 bytes. type: string objective: description: Objective is the campaign goal as the provider names it. Optional, bounded to 1024 bytes. type: string platform: description: 'Platform is the ad network: meta, google, tiktok or x. Empty defaults to meta.' type: string spend: description: Spend is the amount spent so far in MINOR units (cents). Negative values clamp to 0. format: int64 type: integer status: description: 'Status is the lifecycle state: draft, active, paused or completed. Empty defaults to draft.' type: string type: object ad.campaignList: properties: data: description: Data is the matching campaigns, newest-updated first. items: $ref: '#/components/schemas/ad.AdCampaign' type: array type: object ad.updateCampaignIn: properties: account: description: Account is the provider ad-account this campaign runs on (Meta act_). Optional. type: string budget: description: Budget is the campaign budget in MINOR units (cents). Negative values clamp to 0. format: int64 type: integer name: description: Name is the campaign's display label. Required; trimmed and bounded to 1024 bytes. type: string objective: description: Objective is the campaign goal as the provider names it. Optional, bounded to 1024 bytes. type: string platform: description: 'Platform is the ad network: meta, google, tiktok or x. Empty defaults to meta.' type: string spend: description: Spend is the amount spent so far in MINOR units (cents). Negative values clamp to 0. format: int64 type: integer status: description: 'Status is the lifecycle state: draft, active, paused or completed. Empty defaults to draft.' type: string type: object affiliate.affiliateBoard: properties: leaders: description: Leaders are the top opt-in affiliates, by handle and aggregate figures only. items: $ref: '#/components/schemas/affiliate.leaderboardRow' type: array total: description: 'Total is the approved population where it is known; omitted where the top page truncated and the caller has no rank to derive it from.' format: int64 type: integer you: $ref: '#/components/schemas/affiliate.leaderboardRow' description: 'You is the caller''s own row with its exact global rank; only an approved affiliate has one.' type: object affiliate.affiliateEarnings: properties: accruedCents: description: AccruedCents is lifetime commission accrued, in cents. format: int64 type: integer byPeriod: description: 'ByPeriod is the per-period ledger: the margin earned against and the commission taken from it.' items: $ref: '#/components/schemas/affiliate.periodEarningView' type: array byReferredOrg: description: "ByReferredOrg is each referral's aggregate contribution \u2014 the affiliate's\nOWN share, never the\ \ referred org's spend." items: $ref: '#/components/schemas/affiliate.orgEarningView' type: array isAffiliate: description: "IsAffiliate says whether the caller org has an affiliate record. On false it\nis the ONLY field present\ \ \u2014 there is no ledger to report, and the zeros you\nmight expect are absent rather than reported as earnings\ \ of nothing." type: boolean marginBps: description: MarginBps is the platform gross-margin fraction commission is a rate OF. format: int64 type: integer paidCents: description: PaidCents is lifetime commission already paid out, in cents. format: int64 type: integer pendingCents: description: "PendingCents is accrued minus paid \u2014 what the platform still owes." format: int64 type: integer type: object affiliate.affiliateLinks: properties: isAffiliate: description: "IsAffiliate says whether the caller org has an affiliate record. On false only\nmaxLinks comes back\ \ \u2014 there are no links, and there is no link to mint until\nthe org applies and is approved." type: boolean links: description: Links is the caller's share links, each with its URL and funnel. items: $ref: '#/components/schemas/affiliate.codeView' type: array maxLinks: description: MaxLinks is how many share links one affiliate may hold. format: int64 type: integer status: description: 'Status is the caller''s affiliate status: "applied", "approved" or "suspended"; absent for a non-affiliate. Minting a link requires "approved", because a link that cannot accrue quietly loses the referral.' type: string type: object affiliate.affiliateSelf: properties: accruedCents: description: "AccruedCents is lifetime commission accrued, in cents. It only grows \u2014 a\npayout is recorded\ \ against paidCents and never reduces this." format: int64 type: integer code: description: 'Code is the minted referral code, the slug the ?aff link carries. Absent until staff approve; codes live in ONE global namespace across all affiliates.' type: string defaultRateBps: description: 'DefaultRateBps is the direct rate a new affiliate starts at, in basis points of margin (2000 = 20%). Answered ONLY to a caller that has not applied, as the quote beside `schedule`.' format: int64 type: integer downlineTotal: description: DownlineTotal counts every org in the caller's downline across the levels. format: int64 type: integer handle: description: 'Handle is the opt-in public leaderboard name. Empty means opted out: the caller keeps its rank and still sees its own row, it is just not listed.' type: string id: description: 'ID is the affiliate''s server-minted handle, "aff_"-prefixed. Absent until the org applies.' type: string isAffiliate: description: 'IsAffiliate says whether the caller org has an affiliate record. On false the answer carries the rate SCHEDULE and the default rate instead of a downline, so the console can show what the caller would earn.' type: boolean levels: description: Levels is the caller's downline per upline level, with the rate paid there. items: $ref: '#/components/schemas/affiliate.levelView' type: array link: description: 'Link is the shareable ?aff URL built from the code. Empty until a code is minted, since there is nothing to share before approval.' type: string marginBps: description: 'MarginBps is the platform gross-margin fraction, in basis points, that every rate here is a rate OF. Read live per request, so it is the value in force now, not the one that applied to commission already accrued.' format: int64 type: integer paidCents: description: "PaidCents is lifetime commission already paid out, in cents \u2014 credits grants\nand record-only\ \ cash disbursements alike." format: int64 type: integer payouts: description: Payouts is the payout history, newest first, bounded to the last 100 rows. items: $ref: '#/components/schemas/affiliate.remittance' type: array pendingCents: description: "PendingCents is accrued minus paid, in cents \u2014 what the platform still owes\nand the ceiling\ \ on the next payout. Never negative." format: int64 type: integer rateBps: description: 'RateBps is the caller''s OWN direct (level 1) commission rate, in basis points of margin. Levels 2 and 3 are platform-wide and appear in `levels`.' format: int64 type: integer schedule: description: Schedule is the rate schedule quoted to a caller that has not applied. items: $ref: '#/components/schemas/affiliate.levelView' type: array status: description: 'Status is "applied", "approved" or "suspended"; absent for a caller that never applied. Only "approved" mints links and accrues.' type: string type: object affiliate.affiliateStanding: properties: accruedCents: description: AccruedCents is lifetime commission accrued, in cents. format: int64 type: integer code: description: Code is the minted referral code; empty until staff approve. type: string defaultRateBps: description: 'DefaultRateBps is the direct rate a new affiliate would get, answered only to a caller that has not applied.' format: int64 type: integer handle: description: Handle is the opt-in public leaderboard name; empty means opted out. type: string id: description: "ID is the affiliate's server-minted handle, \"aff_\"-prefixed \u2014 what staff\napprove, suspend,\ \ re-rate and pay against. Absent until the org applies." type: string isAffiliate: description: "IsAffiliate says whether the caller org has an affiliate record at all. It is\nthe ONE field an org\ \ that never applied gets besides defaultRateBps: on false,\nread nothing else here \u2014 every other field is\ \ absent, not zero." type: boolean link: description: Link is the shareable ?aff URL; empty until a code is minted. type: string marginBps: description: MarginBps is the platform gross-margin fraction commission is a rate OF. format: int64 type: integer paidCents: description: PaidCents is lifetime commission already paid out, in cents. format: int64 type: integer payouts: description: Payouts is the payout history, newest rows bounded. items: $ref: '#/components/schemas/affiliate.remittance' type: array pendingCents: description: "PendingCents is accrued minus paid \u2014 what the platform still owes." format: int64 type: integer rateBps: description: RateBps is the affiliate's own direct commission rate, in basis points. format: int64 type: integer referredCount: description: ReferredCount is how many orgs this affiliate has referred. format: int64 type: integer requestedCode: description: "RequestedCode is the vanity code asked for at apply time \u2014 a request, not an\nallocation. Approval\ \ mints `code`, which may be a different slug if this one\nwas already taken." type: string status: description: 'Status is "applied", "approved" or "suspended". Only an approved affiliate has a code that resolves for attribution and accrues commission; suspended keeps what it already earned but stops earning more.' type: string type: object affiliate.application: properties: code: description: "Code is the minted referral code. Empty on a first apply \u2014 applying does not\nmint a code, approval\ \ does; a re-apply echoes whatever the row already holds." type: string created: description: "Created says whether THIS call made the row. false means the org had already\napplied and nothing\ \ changed \u2014 no second row, no reset of an existing approval.\nThe HTTP status states the same fact: 201 when\ \ true, 200 when false." type: boolean id: description: "ID is the affiliate's server-minted handle, \"aff_\"-prefixed \u2014 the id staff\napprove, suspend,\ \ re-rate and pay against." type: string rateBps: description: 'RateBps is the direct (level 1) commission rate the row carries, in basis points OF Hanzo''s margin (2000 = 20% of margin, never of the customer''s bill).' format: int64 type: integer requestedCode: description: 'RequestedCode echoes the vanity code asked for, normalized to lower case. It is a request only: approval mints a different slug if this one is taken.' type: string status: description: 'Status is "applied" for a row this call created. A re-apply echoes the existing row''s status, which may already be "approved" or "suspended".' type: string type: object affiliate.applyRequest: properties: requestedCode: description: 'RequestedCode is the vanity code the applicant asks for; approval may mint a different one if it is taken. Body-only: the URL cannot supply it.' type: string type: object affiliate.attributeRequest: properties: code: description: 'Code is the affiliate code the referred org arrived with. Body-only: the URL cannot supply it.' type: string type: object affiliate.attribution: properties: code: description: "Code is the affiliate code the edge was recorded under, normalized to lower\ncase. On a re-post it\ \ is the code of the STANDING edge, which may differ from\nthe one just sent \u2014 first touch wins." type: string created: description: 'Created says whether THIS call made the edge. false means the caller org was already attributed and nothing moved. The HTTP status says the same: 201 when true, 200 when false.' type: boolean createdAt: description: 'CreatedAt is when the edge was FIRST recorded, Unix seconds UTC. On a re-post it is the original time, not now.' format: int64 type: integer id: description: ID is the attribution edge's server-minted handle, "afr_"-prefixed. type: string type: object affiliate.clickCount: properties: counted: description: "Counted says the in-memory buffer took the ping. It does NOT say the code\nexists \u2014 this is deliberately\ \ not a code-existence oracle, and an unknown code\nsimply no-ops at flush time. false means the buffer was full\ \ and the ping was\ndropped, which is harmless: clicks are vanity and move no money." type: boolean type: object affiliate.clickRequest: properties: code: description: 'Code is the share-link code that was clicked. Body-only: the URL cannot supply it.' type: string type: object affiliate.codeView: properties: clicks: description: 'Clicks is how many pings this code has taken. The one STORED counter here and pure vanity: no accrual or payout reads it, pings are coalesced in memory and flushed in batches, and a dropped tally is accepted rather than contending with the money write path. Do not reconcile it against anything.' format: int64 type: integer code: description: "Code is the link's slug \u2014 3\u201332 chars of a\u2013z, 0\u20139 and hyphen \u2014 unique across\n\ the WHOLE directory, so any affiliate's code resolves an attribution." type: string conversions: description: "Conversions is how many of those signups have actually produced positive\ncommission for the caller.\ \ Also derived, from the accrual rows, so it is\n\u2264 signups and lags a referral until the first sweep after\ \ it spends." format: int64 type: integer createdAt: description: CreatedAt is when the link was minted, Unix seconds UTC. format: int64 type: integer label: description: 'Label is the caller''s own note for the link ("twitter", "newsletter"). Cosmetic: trimmed, stripped of control characters, capped at 48 bytes, and never part of the code. "primary" on the link mirrored at approval.' type: string signups: description: "Signups is how many orgs were attributed with this code \u2014 DERIVED by counting\nattribution edges,\ \ never stored, so it cannot drift from the ledger." format: int64 type: integer url: description: 'URL is the full shareable link, the brand host plus ?aff=. The host is the deployment''s own brand, so a Lux or Zoo install never mints a hanzo.ai link.' type: string type: object affiliate.createLinkRequest: properties: code: description: 'Code is an optional vanity code; it must be free across the whole directory, and omitting it mints a random one. Body-only.' type: string label: description: "Label is cosmetic \u2014 trimmed, stripped of control characters, capped \u2014 and\nnever part of\ \ a code. Body-only: the URL cannot supply it." type: string type: object affiliate.handleRequest: properties: handle: description: 'Handle is the public leaderboard display name; empty opts out. Body-only: the URL cannot supply it.' type: string type: object affiliate.handleSet: properties: handle: description: 'Handle is the display name as STORED, echoed back after trimming. Empty means the caller opted out: it keeps its rank and still sees its own row, it is just no longer listed to anyone else.' type: string type: object affiliate.leaderboardRow: properties: accruedCents: description: 'AccruedCents is that affiliate''s lifetime commission accrued, in cents, and what the board is ordered by. An aggregate: no per-customer figure is exposed.' format: int64 type: integer handle: description: "Handle is the affiliate's self-chosen display name \u2014 the only identity the\nboard ever carries.\ \ The org behind it is never disclosed." type: string isYou: description: 'IsYou marks the caller''s own row, so a client can highlight it without matching on a handle. Absent on every other row.' type: boolean rank: description: 'Rank is the position in the GLOBAL approved set ordered by lifetime accrued commission, 1-based. Affiliates that set no handle still occupy their rank and are simply not listed, so the visible ranks have gaps and the board is not a complete roster. On the caller''s own row the rank is computed over the whole set, so it is exact well outside the top page.' format: int64 type: integer referredCount: description: "ReferredCount is how many orgs that affiliate directly referred \u2014 a count\nonly, never which\ \ orgs." format: int64 type: integer type: object affiliate.levelView: properties: downlineCount: description: 'DownlineCount is how many orgs sit exactly this many hops below the caller. It is 0 in the schedule quoted to a caller that has not applied, which has no downline to count.' format: int64 type: integer level: description: 'Level is the upline distance from the org whose spend is being shared: 1 is the direct referrer, 2 and 3 the referrers above it. Nothing accrues past 3.' format: int64 type: integer rateBps: description: 'RateBps is the commission paid at this level, in basis points OF Hanzo''s margin (2000 = 20% of margin, never of the customer''s bill). Level 1 is the affiliate''s own negotiated rate; 2 and 3 are platform switches read live, so this is the schedule actually in force, not one compiled in.' format: int64 type: integer type: object affiliate.linkMint: properties: link: $ref: '#/components/schemas/affiliate.codeView' description: "Link is the link just minted, with its full shareable URL. Its funnel counters\nall start at zero\ \ \u2014 nothing has clicked or signed up through it yet." type: object affiliate.orgEarningView: properties: commissionCents: description: 'CommissionCents is what the caller earned from that org across ALL periods, in cents. Deliberately the caller''s own share and nothing else: that org''s spend and the margin on it are not restated here.' format: int64 type: integer referredOrg: description: "ReferredOrg is the org slug this contribution came from \u2014 one the caller\nreferred, directly\ \ or up to three levels down." type: string type: object affiliate.periodEarningView: properties: commissionCents: description: "CommissionCents is what the caller earned that period, in cents: the sum over\neach referred org and\ \ upline level of margin \xD7 that level's rate. Always \u2264\nmarginCents, by construction." format: int64 type: integer marginCents: description: "MarginCents is the margin Hanzo earned in that period on the spend of every\norg the caller referred,\ \ in cents \u2014 the base commission is a rate OF. It is\nthe aggregate base, never any one customer's bill." format: int64 type: integer period: description: 'Period is the accrual bucket: the UTC year-month, "YYYY-MM". Commission is latched at most once per referred org per period, so one row is one month.' type: string type: object affiliate.remittance: properties: amountCents: description: 'AmountCents is the amount disbursed, in cents. It was reserved against pending commission atomically when recorded, so it never exceeds what was owed.' format: int64 type: integer createdAt: description: "CreatedAt is when the payout was recorded, Unix seconds UTC \u2014 when the balance\nmoved, not necessarily\ \ when the cash landed." format: int64 type: integer id: description: ID is the payout row's server-minted handle, "apo_"-prefixed. type: string method: description: "Method is how it was settled. \"credits\" issued a commerce grant into the\naffiliate org's own wallet;\ \ any other value (wire, paypal, check, \u2026) is a\nRECORD of cash a human moved out of band." type: string reference: description: "Reference is the operator's settlement note \u2014 a bank id, a ledger ref. Free\ntext, absent when\ \ none was given." type: string txn: description: 'Txn is the commerce ledger transaction id, set ONLY where a "credits" payout actually issued the grant. Absent for cash methods, which write no ledger row.' type: string type: object agent.CodingBlob: properties: binary: description: Binary marks content that is not text; it comes back base64. type: boolean content: description: Content is the file's bytes, empty when Truncated. type: string encoding: description: 'Encoding is how Content is carried: "utf8" verbatim, or "base64".' type: string path: description: Path is the file's repo-relative path. type: string size: description: Size is the file's byte length in the repository, whatever was returned below. format: int64 type: integer truncated: description: Truncated marks a file past the 1 MiB view cap. No content is sent. type: boolean type: object agent.CodingChanges: properties: base: description: 'Base is the branch the run started from: the one it was given, or the repository''s default.' type: string commits: description: Commits are the commits on head that base does not have, newest first. items: $ref: '#/components/schemas/agent.CodingCommit' type: array files: description: 'Files is the net change head makes against its merge base with base, one entry per file.' items: $ref: '#/components/schemas/agent.CodingFile' type: array head: description: Head is the run's own branch. Empty for a plan or a setup, which write none. type: string moreCommits: description: 'MoreCommits says the branch carries more commits than the newest 250 that Commits lists.' type: boolean moreFiles: description: "MoreFiles says the change is larger than one read answers \u2014 past 8 MiB of\ndiff or 3000 files\ \ \u2014 so Files lists the files before that, and the last of\nthem is marked truncated with the counts of the\ \ part read." type: boolean pull: $ref: '#/components/schemas/agent.CodingPull' description: "Pull is the run's pull request on the forge, or null while it has none \u2014\nand for a run proposed\ \ somewhere else, a repository the forge only mirrors\nfrom GitHub." repo: description: Repo is the run's repository on the forge, owner/name. type: string type: object agent.CodingCommit: properties: author: description: 'Author is the commit author''s name as git recorded it; a run''s own commits are authored by its harness.' type: string date: description: Date is the author date, RFC 3339 in UTC. type: string message: description: "Message is the commit's subject \u2014 its first line only." type: string sha: description: SHA is the full commit hash. type: string type: object agent.CodingEntry: properties: name: description: Name is the entry's own name, no directory part. type: string path: description: Path is the entry's full repo-relative path. type: string size: description: Size is a file's byte length; 0 for a directory. format: int64 type: integer type: description: Type is "tree" for a directory and "blob" for anything else a reader opens. type: string type: object agent.CodingFile: properties: additions: description: 'Additions counts the lines the change adds, over the whole file whether or not all of its patch is carried.' format: int64 type: integer deletions: description: Deletions counts the lines the change removes, the same way. format: int64 type: integer from: description: From is where a renamed file was. Omitted for every other status. type: string patch: description: 'Patch is the file''s unified-diff hunks, from its first "@@". Empty for a binary file and for a rename that moved no line.' type: string path: description: "Path is where the file is on the branch \u2014 for a deleted file, where it was." type: string status: description: Status is added, modified, deleted or renamed. type: string truncated: description: 'Truncated marks a patch cut short: at 64 KiB for one file, or past 1 MiB of patch across the change, when later files carry none. The counts are whole.' type: boolean type: object agent.CodingPull: properties: mergeable: description: 'Mergeable is the forge''s verdict that the base takes the branch without conflict.' type: boolean moreReviews: description: MoreReviews says it has more reviews than the first 50 that Reviews lists. type: boolean number: description: Number is the pull request's number in its repository. format: int64 type: integer reviews: description: 'Reviews are its reviews, oldest first: the first 50.' items: $ref: '#/components/schemas/agent.CodingReview' type: array state: description: State is open, closed or merged. type: string title: description: Title is its title. type: string url: description: URL is where a person reads and merges it. type: string type: object agent.CodingReview: properties: at: description: 'At is when it was submitted, RFC 3339 in UTC; empty for one that has not been.' type: string author: description: Author is the reviewer's forge login. type: string body: description: 'Body is what the reviewer wrote; empty for a bare verdict. At most 16 KiB of it, and 256 KiB across the reviews listed.' type: string state: description: 'State is the verdict: approved, request_changes, comment, pending or request_review.' type: string truncated: description: "Truncated marks a Body cut at either bound \u2014 or left empty past the second,\nwhere the review\ \ keeps its author, verdict and time." type: boolean type: object agent.CodingStartIn: properties: after: description: "After names a previous run's session, and starts this one from where that\none stopped instead of\ \ from the repository's default. It is how a follow-up\ninstruction \u2014 \"now add tests for it\" \u2014 builds\ \ on work already done rather\nthan beginning again on a fresh clone.\n\nIt sets the base and nothing else, so\ \ this run still writes its OWN branch.\nOne run, one branch: a run that wrote back onto an earlier run's branch\n\ would break the rule the forge's ref policy is built on, and would leave\ntwo turns of work with one name to review.\n\ \nA caller who already knows the branch may pass Base directly; this exists\nbecause the branch is derived from\ \ a session id and nobody should have to\nknow how. Base wins if both are given." type: string agentRef: description: 'AgentRef names a configured agent to run as, which is how an org pins a harness, a model and a prompt to a name. Empty runs the default agent.' type: string base: description: "Base is the branch to start from. Empty takes the repository's default. The\nrun never writes here\ \ \u2014 it writes the agent branch it answers with." type: string desktop: description: "Desktop asks for a run with a SCREEN \u2014 an image carrying an X server \u2014 for a\ntask that\ \ has to drive a browser or another windowed program. False, the\ndefault, is a headless checkout, which is what\ \ writing code needs." type: boolean effort: description: 'Effort is how hard that model reasons: low, medium or high. Empty takes the model''s own default. Taken by the dev and codex harnesses, like Model.' type: string issue: description: 'Issue is the number of the issue on Repo this run resolves. Its pull request says `Closes #`, so merging the work closes the issue. Zero is a run no issue asked for.' format: int64 type: integer mode: description: "Mode is what the run may do to the repository: `build` edits, commits and\npushes its branch; `plan`\ \ reads the repository and answers with a plan,\nwriting nothing \u2014 its sandbox is handed a credential that\ \ can only read and\nno push step runs, and the plan arrives as the run's final status. `setup`\nis a plan put\ \ to one question: its agent explores the checkout, installs and\nchecks what it finds, and ends with the codebase's\ \ environment, which is kept\nas the proposal at /v1/environment/{repo} until someone saves it. Empty is\n`build`.\ \ A plan or a setup runs in our sandbox only: a claimed machine clones\nand pushes with its own credential, so\ \ one routed to a machine is refused." type: string model: description: 'Model is the model the run''s agent thinks with, by the id GET /v1/models lists. Empty takes the platform''s coding model. The dev and codex harnesses take it; the others do not think with a model we name, so naming one for them is refused.' type: string project: description: 'Project scopes the run to one board''s work when the org keeps more than one. Empty is the org''s default.' type: string prompt: description: 'Prompt is the task, in the words you would use with a colleague who has the checkout open. It is the whole instruction: there is no second field for context, and a prompt that names files and the outcome it wants gets a run that does not have to guess either.' type: string reply: $ref: '#/components/schemas/agent.Reply' description: "Reply is WHERE THE RUN NARRATES ITSELF, when the surface that started it has\nsomewhere for it to\ \ talk. Empty means nobody is listening and the run simply\ndoes not narrate \u2014 which is the app surface's\ \ case, because /v1/agent/coding\nhands back a session id and the session stream is a better progress feed than\n\ any message could be.\n\nIT IS NOT A FIELD A MODEL FILLS IN. A run asked for in a conversation is\ndispatched\ \ with the address that conversation is happening at, written over\nwhatever arrived here (apps/agents/replyto.go).\ \ A model naming a channel is\na model naming a room it was not spoken to in, so the value it chose is\ndiscarded\ \ rather than argued with \u2014 which is the same rule that left the org\nand the subject off this type entirely." repo: description: "Repo is what to work on: a repository's name, `owner/name` in the caller's\nown org, or the site a\ \ repository serves (`hanzo.ai`), as the person said\nit. The engine resolves it against the org's own repositories\ \ \u2014 a name that\nmatches none is refused with the nearest ones \u2014 and finds the clone URL and\nthe push\ \ credential from the org itself, so this says WHICH repository and\nnever how to reach it.\n\nA repository on\ \ GitHub is named by GitHub's address, `github.com/owner/name`\n\u2014 or `owner/name` when the owner is not the\ \ org's own \u2014 and runs there\ndirectly: it is admitted when the caller's own connected GitHub may push to\n\ it, or when the caller administers the org and the org's installation holds\nit, and refused otherwise.\n\nEmpty\ \ starts something new: the run makes a project named from the prompt,\nunder a slug the org does not already\ \ hold, and works in a repository of\nthe same name." type: string targetId: description: 'TargetID routes the run to a registered machine the org has claimed instead of to a sandbox in our cluster. Empty runs it here, which is the usual case.' type: string task: description: 'Task is Prompt under the name a caller asking for work tends to reach for. The two are one field: Prompt wins when both are given.' type: string timeoutSeconds: description: 'TimeoutSeconds bounds the whole run. Unset takes the default budget; a run that hits the bound is stopped and reports what it had done by then.' format: int64 type: integer tool: description: "Tool is which harness runs the prompt \u2014 dev | claude | codex | python |\nnode \u2014 and Desktop\ \ is whether the run needs a screen. Both are empty by\ndefault, which is `dev` with no screen, and that default\ \ is what every\ncaller gets until it says otherwise.\n\nThey are two fields because they are two questions. The\ \ harness decides\nwhat argv starts; the screen decides which image carries an X server. A\ncaller may want claude\ \ WITH a browser it can see, and a single enum would\nhave made that combination unsayable." type: string type: object agent.CodingStarted: properties: branch: description: 'Branch is the ref the run will push its work to, and the ONLY ref it is permitted to write. It exists before the work does, so it is safe to tell somebody where to look while the run is still going.' type: string project: description: 'Project is the slug of the project a run with no repo made for itself, the handle /v1/project/{slug} answers to. Empty when the run was given a repo.' type: string repo: description: 'Repo is the repository the run was admitted against, echoed back as the engine resolved it.' type: string routed: description: 'Routed says the run went to one of the org''s own registered machines rather than to a sandbox in our cluster. False is the ordinary case.' type: boolean sessionId: description: 'SessionID is the run''s handle: its durable record, and the root its live progress streams under at /v1/agent/sessions/stream?root=. Every later question about this run is asked with it.' type: string targetId: description: TargetID names that machine when Routed is true, and is empty otherwise. type: string type: object agent.CodingTree: properties: entries: description: Entries are the directory's immediate children, directories first. items: $ref: '#/components/schemas/agent.CodingEntry' type: array ref: description: 'Ref is the branch this was read at: the run''s own once the forge holds it, its base until then.' type: string type: object agent.GPU: properties: memory: description: VRAM bytes, 0 = unknown format: int64 type: integer model: description: '"GB10", "8060S", "RTX 4090"' type: string vendor: description: nvidia | amd | apple | intel | ... type: string type: object agent.McpTools: properties: isEnabled: type: boolean serverName: type: string tools: type: string type: object agent.Metrics: properties: at: description: unix seconds, server-stamped format: int64 type: integer cpuTemp: description: "CPUTemp is the CPU package temperature in \xB0C." format: double type: number cpuUtil: description: CPUUtil is the busy fraction of all cores over the sample window, 0..1. format: double type: number decode: description: Decode is generation throughput over the sample window, in tokens per second. format: double type: number diskRead: description: DiskRead is the block-device read rate in bytes per second. format: double type: number diskTotal: description: DiskTotal is the size of those filesystems, in bytes. format: int64 type: integer diskUsed: description: DiskUsed is the space used across local block filesystems, in bytes. format: int64 type: integer diskWrite: description: DiskWrite is the block-device write rate in bytes per second. format: double type: number gpuMemTotal: description: GPUMemTotal is dedicated VRAM in bytes; absent when the GPU shares system memory (unified). format: int64 type: integer gpuMemUsed: description: GPUMemUsed is the memory held by GPU processes, in bytes. format: int64 type: integer gpuPower: description: GPUPower is the total GPU power draw in watts. format: double type: number gpuPowerLimit: description: GPUPowerLimit is the total enforced GPU power limit in watts; absent when the driver reports none. format: double type: number gpuTemp: description: "GPUTemp is the hottest GPU's temperature in \xB0C." format: double type: number gpuUtil: description: 0..1 aggregate utilization format: double type: number kvCache: description: KVCache is the KV-cache occupancy, 0..1. format: double type: number load1: description: "Load1 is the machine's own one-minute load average \u2014 a count of runnable and\nuninterruptible\ \ tasks, NOT a percentage and NOT already divided by core\ncount, so it is read against Spec.CPUs: 8.0 is idle\ \ on 16 cores and swamped\non 4. Coerced finite and non-negative on write, so 0 means either genuinely\nidle or\ \ nothing reported." format: double type: number load5: description: Load5 is the same figure averaged over five minutes. format: double type: number load15: description: "Load15 is the same figure over fifteen. The three together are what separate\na machine that is busy\ \ right now from one that has been busy all along \u2014\nwhich is the question a dispatcher is really asking." format: double type: number memFree: description: bytes format: int64 type: integer memUsed: description: bytes format: int64 type: integer model: description: Model is the model id(s) served on this machine, comma-separated, at most 128 bytes. type: string netRx: description: NetRx is the receive rate of physical interfaces in bytes per second. format: double type: number netTx: description: NetTx is the transmit rate of physical interfaces in bytes per second. format: double type: number prefill: description: Prefill is prompt (prefill) throughput over the sample window, in tokens per second. format: double type: number running: description: Running is the number of requests being served now. format: int64 type: integer ttft: description: TTFT is the mean time to first token, in seconds, of requests that started in the window. format: double type: number waiting: description: Waiting is the number of requests queued. format: int64 type: integer type: object agent.Reply: properties: channel: description: 'Channel is the room inside that workspace, and it is what decides whether there is an address at all. Empty is the ordinary case: a caller of /v1/agent/coding holds a session id and reads the stream instead.' type: string provider: description: "Provider is the transport the conversation is on \u2014 slack, discord,\ntelegram, teams \u2014 and\ \ it is what decides who can carry the text at all. An\naddress without it names a room in no particular workspace." type: string thread: description: "Thread narrows the address to one thread inside the channel \u2014 on Slack the\nparent message's\ \ ts, the same value a reply carries as thread_ts. Empty\nputs the text at the top level of the channel." type: string turn: description: "Turn is the turn that renders a run started here: the session whose one\nmessage the run's progress\ \ and its outcome are shown in. A run with a Turn\nposts nothing of its own \u2014 one question, one message.\ \ The dispatcher writes\nit (apps/agents/replyto.go); a model never does." type: string user: description: "User is the platform's id for the person the conversation is with \u2014 whose\nrun it is. A run's\ \ buttons name them to anyone else who presses one; it is\nnever what decides who may (that is the chat identity)." type: string type: object agent.Spec: properties: arch: description: amd64 | arm64 | ... type: string cpus: description: logical cores format: int64 type: integer gpus: description: "GPUs is every accelerator the machine advertises, one entry each, capped at\n32 on write. Empty means\ \ the probe found none \u2014 and that is the answer a\nNeed is checked against, so a machine with no entry here\ \ clears no\naccelerator floor. The list is not vendor-filtered: what satisfies a job is\ncounts and VRAM, never\ \ a brand (see Need)." items: $ref: '#/components/schemas/agent.GPU' type: array memory: description: total RAM, bytes format: int64 type: integer os: description: linux | darwin | windows type: string type: object agent.activityFeed: properties: activity: description: Activity is the merged run/create/update events, newest first, capped at 50. items: $ref: '#/components/schemas/agent.activityView' type: array type: object agent.activityView: properties: agent: description: agent name type: string at: description: RFC3339 UTC type: string id: description: "ID identifies the event, and its shape says which kind it is: a run event\ncarries the run's own id,\ \ while an agent event is the agent id suffixed\n\":created\" or \":updated\". Unique within a feed, and not an\ \ address \u2014 there is\nnothing to fetch it by." type: string kind: description: invoked|failed|created|updated (from real events) type: string message: description: "Message is the line to render, already bounded: \"Invoked \" for a run\nthat worked, the run's\ \ own error truncated to 200 characters for one that did\nnot (or \"Run failed\" when it said nothing), and a\ \ fixed phrase for the two\nagent events. Nothing here is invented \u2014 every event is a row that exists." type: string type: object agent.agentDetail: properties: avatar: type: string cap_micro_usd: format: int64 type: integer computeRef: type: string consumed_micro_usd: format: int64 type: integer createdAt: type: string description: type: string emoji: type: string executionMode: type: string id: type: string instructions: description: 'Instructions is the agent''s system prompt, verbatim, up to 32 KiB. It is the one field the list read withholds, because it is the agent''s whole behaviour and a page of them would be a page of prompts.' type: string lineage: items: type: string type: array max_task_micro_usd: format: int64 type: integer model: type: string name: type: string parent: type: string period: type: string recentRuns: description: 'RecentRuns is the agent''s 20 most recent executions, newest first. It is a window on the history, not the history: the count beside it is `runs`.' items: $ref: '#/components/schemas/agent.agentRunView' type: array runs: format: int64 type: integer schedule: type: string serviceAccountId: type: string status: type: string tools: items: type: string type: array updatedAt: type: string type: object agent.agentList: properties: agents: description: Agents is the org's agents, each carrying its recorded run count. items: $ref: '#/components/schemas/agent.agentView' type: array type: object agent.agentRunView: properties: actor: description: "Actor is the \"org/sub\" identity the run was executed and billed AS. Empty\nmeans there was no PERSON\ \ \u2014 a schedule or a service token \u2014 which is a\ndifferent fact from \"we do not know\", and the difference\ \ is what an audit\nasks about." type: string agent: description: "What an operator needs to answer \"what ran, for whom, and what did it do\" \u2014\nand, through traceId,\ \ to leave this record for the waterfall of the very\nsame run rather than a search that hopefully lands near\ \ it.\n\nAgent is on the row because the org-wide feed lists runs across agents, and\na run that cannot name its\ \ agent is an orphan in exactly the view built to\nmake sense of many of them. Every field is omitempty: a run\ \ recorded before\nthese columns existed reports absence rather than a zero it never measured." type: string completionTokens: description: 'CompletionTokens is the same measurement for what the model produced, on the same final completion. It is a count of TOKENS, not of turns and not of money.' format: int64 type: integer createdAt: description: "CreatedAt is when the run finished, RFC 3339 in UTC to the second \u2014 the\nduration above already\ \ says how long it had been going." type: string durationMs: description: 'DurationMs is wall-clock milliseconds around the completion, including a failover''s retries. It is time SPENT, not time billed.' format: int64 type: integer error: description: 'Error is why an "ok"-less run failed, as the failing call reported it. Empty on every successful run.' type: string id: description: 'ID is the run''s handle, minted as "run_" + 32 hex characters. It is the key the metering ledger records this run''s per-round token spend under, so it is how a bill and a run are joined.' type: string input: description: Input is the text the run was given, verbatim. type: string micro_usd: description: MicroUSD is what the run spent, integer micro-USD. Absent when nothing was metered. format: int64 type: integer model: description: "Model is the model that actually SERVED this run, which is not always the one\nthe agent is defined\ \ on \u2014 a failover records what answered. Normalized to our\nname on the way out; the stored row is left exactly\ \ as it happened, because a\nrun is a record and rewriting it would be worse than the name it carries." type: string output: description: "Output is what the model produced. Empty on an error run, and empty is also a\nlegitimate answer from\ \ a run that succeeded with nothing to say \u2014 Status is\nwhat separates those." type: string promptTokens: description: "PromptTokens is what the gateway reported for the run's FINAL completion, and\nonly that one \u2014\ \ a tool loop's earlier rounds are the metering ledger's account,\njoined by this run's id. Reading it as the\ \ run's total spend undercounts a\nloop." format: int64 type: integer status: description: 'Status is the run''s outcome, and there are exactly two: "ok" when the model answered, "error" when it did not. It is written when the run ends, so no row here is in flight.' type: string toolCalls: description: "ToolCalls is how many tool dispatches the run made \u2014 a count of ACTIONS, which\nis a different\ \ measurement from the token counts above and from the turns a\nbuild reports. Zero is a run that answered straight\ \ from the model." format: int64 type: integer traceId: description: 'TraceID is the trace this run IS, so the record and its spans are one thing to move between: it opens the waterfall for THIS run rather than a search that lands near it. Empty when the process had no tracer, never a fabricated id.' type: string type: object agent.agentView: properties: avatar: description: "Avatar is an image the agent is drawn as \u2014 a link to one, or the bytes\ninline as a data URL,\ \ up to 96 KiB. Emoji is the one glyph a caller picked\nwhen they had no image. At most one is ever set; neither\ \ means the agent is\ndrawn as its initial, the same way a person with no photo is. Both are\niam/pkg/schema's\ \ Mark, so a face means the same thing on an agent as it\ndoes on a person or an org.\nAvatar is the agent's picture:\ \ an image URL, or the image itself inline as a\ndata URL up to 96 KiB. Empty when the agent has no image." type: string cap_micro_usd: description: 'CapMicroUSD is the total this agent may spend within one Period, as an integer number of micro-USD (1,000,000 = $1). It is required at creation: a cap of zero would mean no limit and no per-agent spend record at all.' format: int64 type: integer computeRef: description: 'ComputeRef is the visor machine this bot is bound to, opaque here: this package stores and echoes it, and the binding''s lifecycle belongs elsewhere. Empty means unbound, which is what every one-shot agent is.' type: string consumed_micro_usd: description: 'ConsumedMicroUSD is what has been spent in the current period, in micro-USD. It is settled from what the gateway reported, not from the quote.' format: int64 type: integer createdAt: description: CreatedAt is when the agent was defined, RFC 3339 in UTC to the second. type: string description: description: "Description is the one line another agent reads when deciding whether to call\nthis one: the tool\ \ catalogue publishes it as the description of `agent_`,\nfalling back to \"agent \" when it is empty.\ \ It is not part of the prompt \u2014\nInstructions is \u2014 so writing the behaviour here reaches the caller\ \ and not the\nmodel." type: string emoji: description: 'Emoji is the single glyph a caller picked when they had no image. At most one of avatar and emoji is ever set; neither means the agent is drawn as its initial, the same way a person with no photo is.' type: string executionMode: description: 'ExecutionMode is one-shot or long-running, and it decides who may start this agent. one-shot runs only when something POSTs to it; long-running is additionally invoked by the scheduler on Schedule, once a minute against the cron. An org''s long-running agents are capped, so a switch INTO it can be refused with 409.' type: string id: description: "ID is the agent's stable handle, minted here as \"agent_\" + 32 hex characters\nof crypto/rand. A\ \ caller cannot choose it, and it never changes \u2014 unlike Name,\nwhich is the other way to address the same\ \ agent." type: string lineage: description: "Lineage is every agent above this one, nearest first \u2014 the parent, its\nparent, and so on to\ \ the agent a person defined. All of them are in this\norg, so the principal this agent answers to is theirs." items: type: string type: array max_task_micro_usd: description: 'MaxTaskMicroUSD is the ceiling for a single run, in micro-USD. A session cannot exceed it even when the period cap still has room, so one runaway task cannot consume a month.' format: int64 type: integer model: description: 'Model is the Zen model this agent runs on, and it is always OUR name for it: writes normalize through cloud.ZenModel and the read normalizes again, so an upstream family name never leaves here even from a row written before that rule existed. A create that named none took the deployment''s configured default, so this is where a caller learns which model it actually got.' type: string name: description: 'Name is the agent''s org-unique handle, matching ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$. It addresses the agent everywhere ID does, it is what a run row records, and it is the suffix of the `agent_` tool other agents call this one by. Set once at create; no update route moves it, because moving it would orphan that history.' type: string parent: description: 'Parent is the agent that spawned this one; absent for an agent a person defined.' type: string period: description: 'Period is the window the cap resets on: day, week or month.' type: string runs: description: "Runs is how many executions the org has recorded against this agent, counted\nat read time. The list\ \ and update reads count the WHOLE history; the detail\nread reports the size of the RecentRuns page it carries,\ \ which stops at 20 \u2014\nso a detail row saying 20 means \"at least 20\", not \"exactly 20\"." format: int64 type: integer schedule: description: "Schedule is the 5-field cron the scheduler fires a long-running agent on,\nevaluated once a minute.\ \ Required for long-running and DROPPED for one-shot \u2014\na one-shot agent's schedule is not stored, so absence\ \ here is the mode's\nanswer rather than a value nobody set." type: string serviceAccountId: description: 'ServiceAccountID is the IAM agent service account (-) a scheduled run is billed AS. It is what makes an autonomous run attributable to a principal rather than only to the org; empty means the org itself wears the spend.' type: string status: description: 'Status is the agent''s readiness, and today it is "ready" on every row: an agent is a definition rather than a provisioned thing, so nothing transitions it. Server-set at create; no route accepts it.' type: string tools: description: 'Tools are the tool names this agent may call, and the list IS the authority: an agent that declares none gets none. The single entry "*" means whatever the fleet''s MCP server serves at the moment of the run, resolved per run rather than frozen here, which is how the default assistant reaches subsystems that shipped after it was defined. Empty array, never null.' items: type: string type: array updatedAt: description: 'UpdatedAt is the last time any field above was written, same format. It moves on an update to the DEFINITION and never on a run, so a busy agent nobody has edited keeps an old one.' type: string type: object agent.buildList: properties: builds: description: Builds is every published build, most recently updated first. items: $ref: '#/components/schemas/agent.buildSummary' type: array type: object agent.buildSummary: properties: agent: description: Agent is the label the surface that did the work calls itself by. type: string endedAt: description: EndedAt is when it finished, same format. Empty means it is still going. type: string org: description: "Org and Project are the build's public ADDRESS \u2014 the pair the full story is\nread at, and the\ \ pair a visitor sees in the URL bar. Not a tenant key: this\nindex is anonymous and lists only what authors published." type: string project: description: Project is the product's slug, the second half of that address. type: string repo: description: Repo is the repository the work was done in, as the session reported it. type: string session: description: 'Session is the agent session behind the build, and the value its commits name in their `Hanzo-Session:` trailer.' type: string startedAt: description: StartedAt is when the session opened, RFC 3339 in UTC. type: string status: description: "Status is the session's own: running, paused, done or error \u2014 so a card can\nshow a build still\ \ being written." type: string title: description: 'Title is the human line for the card. Sent even when empty, like every field here, because that is what this route has always sent.' type: string turns: description: "Turns is HOW MANY turns the transcript holds \u2014 a COUNT, unlike the `turn` on\neach turn of the\ \ full story, which is that turn's position. The full read\nreturns at most 1000 of them; this number is not capped." format: int64 type: integer type: object agent.buildTurn: properties: actor: description: 'Actor is who took the turn. A deploy turn''s actor is the literal "deploy", because nobody took it.' type: string at: description: At is when the turn was recorded, RFC 3339 in UTC to the second. type: string body: description: "Body is the readable text of the turn, taken from the stored event's `text`.\nEmpty when the event\ \ carried a payload of some other shape \u2014 this route reads\ntranscripts and does not invent prose for turns\ \ that are not one." type: string commit: description: 'Commit is the full sha this turn produced, empty when the turn changed nothing. It is ECHOED from the transcript, and the authority is the commit itself: it carries the `Hanzo-Session:`/`Hanzo-Turn:` trailer, or a note under refs/notes/hanzo-provenance saying the same, so the claim is checkable at source with the command in `verify`.' type: string kind: description: 'Kind is what the turn was, from the log''s closed six: message, tool-call, spawn, log, status, control. A deploy arrives as a `status` turn.' type: string subject: description: 'Subject is that commit''s subject line, from the same transcript, so a reader sees what the commit says without fetching the repository.' type: string turn: description: "Seq is this turn's POSITION in the session's log \u2014 monotonic from 1, per\nsession \u2014 and\ \ it is what a commit's `Hanzo-Turn:` trailer names. It is not a\ncount of anything: the count is `turns` on the\ \ summary beside it." format: int64 type: integer type: object agent.buildView: properties: agent: description: Agent is the label the surface that did the work calls itself by. type: string endedAt: description: "EndedAt is when it finished, same format. Empty means it has not \u2014 the build\nis still going." type: string model: description: "Model is the model that did the work, taken from the FIRST turn whose body\nnames one \u2014 a transcript\ \ states it, this route does not resolve it. Empty when\nno turn said." type: string org: description: "Org is the org that published this build, echoed from the URL. It is part of\nthe build's public ADDRESS\ \ and not a tenant key \u2014 this route is anonymous, and\nthe only rows it can reach are ones an author explicitly\ \ published." type: string project: description: Project is the product's slug, the other half of that address. type: string repo: description: Repo is the repository the work was done in, as the session reported it. type: string session: description: "Session is the id of the agent session this story IS \u2014 the same value a\nproduced commit carries\ \ in its `Hanzo-Session:` trailer, which is what ties\nthe repository's history to this page." type: string startedAt: description: StartedAt is when the session opened, RFC 3339 in UTC. type: string status: description: "Status is the session's own: running, paused, done or error. A build can be\nread while it is still\ \ being written, so this is not always terminal \u2014 and an\n`error` build is still a readable story, not a\ \ missing page." type: string title: description: 'Title is the human line the session was opened or renamed with. Empty when nobody gave it one.' type: string turns: description: 'Turns is the whole transcript, oldest first, capped at 1000: a published build is a story to read down, not an archive to page.' items: $ref: '#/components/schemas/agent.buildTurn' type: array verify: description: 'Verify is the exact command that re-derives every commit binding below straight from git, so nothing here has to be taken on trust.' type: string type: object agent.claimKeyOut: properties: claimKey: description: "ClaimKey is the capability itself. It is returned ONCE and never again \u2014 only\nits SHA-256 hash\ \ is stored \u2014 so a daemon that loses it mints a new one." type: string targetId: description: TargetID is the machine the key authenticates. type: string type: object agent.controlCommandView: properties: command: description: "Command is what was asked, from a closed four: pause, resume, stop, message.\nIt is an INTENT \u2014\ \ the poller decides what to do about it, and the session's\nstatus changes only when the poller reports back\ \ that it did." type: string message: description: 'Message is the text that came with the command: what to say into the run for `message`, and the cancellation reason for `stop`. Up to 16 KiB. Empty on a bare pause or resume.' type: string payload: description: 'Payload is the structured half a caller sent instead of (or beside) Message, embedded as JSON. Its shape is the caller''s business; this surface carries it through untouched.' seq: description: "Seq is this command's position in the session's log \u2014 the same monotonic\nnumber every other\ \ turn is ordered by, so a command sits in the transcript\nwhere it was issued. Send the highest one you applied\ \ back as `after` and it\nis never redelivered." format: int64 type: integer type: object agent.controlDrain: properties: commands: description: Commands is the session's control commands newer than the cursor, oldest first. items: $ref: '#/components/schemas/agent.controlCommandView' type: array cursor: description: "Cursor is the seq to send as `after` on the next poll \u2014 the highest seq in\nthis page, or the\ \ cursor sent in when the page is empty." format: int64 type: integer type: object agent.controlIn: properties: id: description: ID is the session to steer, from the path. type: string message: description: 'Message is free text for the running agent, up to 16 KiB. On a stop it is recorded as the cancellation reason.' type: string payload: description: 'Payload is a structured argument for the command: any valid JSON up to 64 KiB, scanned for credentials before it is stored (a hit refuses the command with 422 and the findings). A task-backed session receives it as the forwarded signal''s argument.' type: object agent.controlResult: properties: command: description: 'Command is the verb that was recorded: pause, resume, stop or message.' type: string event: $ref: '#/components/schemas/agent.eventView' description: 'Event is the durable control event the command became. The intent is recorded whether or not it reached an engine, which is what makes a stream-consuming surface able to act on it.' forwarded: description: 'Forwarded is whether the command also reached the durable-execution engine. FALSE IS NOT A FAILURE: a session with no workflow link, or a deployment with no tasks backend, is record-only by design. A forward that was attempted and failed is a 502, never a false here.' type: boolean run: description: 'Run is the run a resume started, under the same session. Empty for every other command.' type: string type: object agent.createAgentIn: properties: avatar: description: "Avatar and Emoji are how the agent APPEARS. An image wins when both are\ngiven \u2014 it is the thing\ \ somebody made \u2014 and both empty leaves the agent\ndrawn as its initial. Validated by iam/pkg/schema, the\ \ same rule a person's\navatar passes, so the 96 KiB bound and the accepted URL forms are stated\nonce for every\ \ subject that has a face." type: string cap_micro_usd: description: 'The budget, required. An agent is not creatable without one: a zero cap would mean no limit and no record of its spend, which is what a budget exists to remove. Integer micro-USD; the CLI converts dollars before sending.' format: int64 type: integer computeRef: description: "ComputeRef optionally binds this bot to a visor machine. Opaque here, bounded\nat 256 characters,\ \ and not resolved \u2014 this package stores the reference and\nthe binding's lifecycle belongs elsewhere." type: string description: description: 'Description is the one line published as the description of the `agent_` tool, which is how another agent decides whether to call this one. Optional, and worth writing for exactly that reason.' type: string emoji: description: "Emoji is the single glyph shown when there is no image. An image WINS when\nboth are given \u2014\ \ it is the thing somebody made \u2014 and both empty leaves the\nagent drawn as its initial." type: string executionMode: description: 'ExecutionMode is one-shot or long-running. Empty takes one-shot, which runs only when something POSTs to it. long-running additionally requires Schedule, and counts against a per-org cap that answers 409 when it is full.' type: string instructions: description: 'Instructions is the system prompt, up to 32 KiB, stored verbatim. This is what the model reads; Description is what other CALLERS read.' type: string max_task_micro_usd: description: 'MaxTaskMicroUSD is the ceiling for a single run, in micro-USD. A session cannot exceed it even when the period cap still has room, so one runaway task cannot consume a month.' format: int64 type: integer model: description: 'Model names the model to run on. Omit it to take the deployment''s configured default; name one and it is checked against the gateway''s served catalogue here, so a model this deployment cannot serve is refused now rather than at the first run. Stored under our own name for it, whatever spelling arrives.' type: string name: description: 'Name is the agent''s org-unique handle and the only required field. It must match ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$, and a name already taken in this org is a 409 rather than an overwrite. It is permanent: no update route moves it.' type: string parent: description: "Parent is the agent spawning this one \u2014 its id or name, in the caller's\norg. The new agent answers\ \ to the org its parent answers to, and a parent\noutside the caller's org is not found: an agent is never spawned\ \ into a\nprincipal its creator does not answer to. Omitted for an agent a person\ndefines. Set once; no update\ \ moves it." type: string period: description: 'Period is the window the cap resets on: day, week or month.' type: string schedule: description: 'Schedule is the 5-field cron a long-running agent fires on, parsed here so a bad expression is a 400 and not an agent that silently never runs. Required with long-running; DISCARDED for one-shot rather than stored unused.' type: string serviceAccountId: description: 'ServiceAccountID optionally names the IAM agent service account (-) a scheduled run should be billed AS, so an autonomous run is attributable to a principal rather than only to the org. Same 256-character bound, also unresolved here.' type: string tools: description: "Tools are the tool names this agent may call. Omitted or empty grants NONE \u2014\nthat default is\ \ the agent's authority and is not widened anywhere. The single\nentry \"*\" means whatever the fleet's MCP server\ \ serves at the time of each run." items: type: string type: array type: object agent.eventIn: properties: actor: description: 'Actor is who produced the turn. Empty takes the validated caller, which is what an agent writing its own transcript wants; naming one is for a surface recording on somebody else''s behalf.' type: string id: description: ID is the session to append to, from the path. type: string kind: description: "Kind is what this turn IS: message, tool-call, spawn, log, status, control\nor progress. Anything\ \ else is refused \u2014 the vocabulary is closed so a\nreader can branch on it." type: string payload: description: "Payload is the turn's own body, any valid JSON up to 64 KiB. It is SCANNED\nfor credentials before\ \ it is stored, and a hit refuses the whole append with\n422 and the findings \u2014 this plane never redacts\ \ a secret into a transcript\nit then keeps.\n\nIts SHAPE is the writer's business for every kind but one: a `progress`\n\ turn is the run reporting on itself in a shape this surface publishes \u2014\n{\"pct\":0-100 (optional), \"phase\"\ :\"running|blocked|done\",\n\"activity\":\"\u2264120 chars\"} \u2014 so a malformed one is refused with 400 instead\n\ of being stored as prose nobody reads." type: object agent.eventView: properties: actor: description: 'Actor is who produced the turn. A write that names nobody takes the calling principal, so this is rarely empty in practice.' type: string createdAt: description: 'CreatedAt is when the turn was recorded, RFC 3339 in UTC to the second. Seconds are coarse enough that two turns can share one, which is why Seq and not this is the order.' type: string id: description: 'ID is the event''s own handle, minted as "evt_" + 32 hex characters. It identifies the turn; Seq is what ORDERS it.' type: string kind: description: 'Kind is what the turn IS, from a closed six: message (a model turn), tool-call, spawn (a subagent started), log, status, control (a steering command the running surface consumes). Anything else is refused at the write.' type: string payload: description: "Payload is the turn's body, embedded as JSON rather than as a string \u2014\nwhatever the writer sent,\ \ up to 64 KiB, checked for well-formedness and\nscanned for credentials before it was stored. Its SHAPE is the\ \ writer's\nbusiness and varies by Kind; this surface does not interpret it, except\nthat every string in it is\ \ kept as plain text: terminal escape sequences\nand control characters other than the line break and the tab\ \ are removed." seq: description: "Seq is the turn's position in this session's log: monotonic from 1, assigned\nby the store inside\ \ the insert, and unique PER SESSION rather than globally.\nIt is the cursor a reader resumes from after a reconnect\ \ \u2014 ask for everything\nafter your last-seen seq." format: int64 type: integer sessionId: description: "SessionID is the session this turn belongs to. Carried on every event so a\nstream frame stands alone\ \ \u2014 a subscriber watching a whole tree gets turns from\nseveral sessions down one connection." type: string type: object agent.lastEventView: properties: actor: description: 'Actor is who produced the turn, defaulted to the calling principal when the writer named nobody.' type: string at: description: At is when the turn was recorded, RFC 3339 in UTC to the second. type: string kind: description: 'Kind is what the turn was, from the log''s closed six: message, tool-call, spawn, log, status, control.' type: string preview: description: "Preview is the first 240 bytes of the event's payload, cut without regard for\nthe JSON inside it\ \ \u2014 it is a string to SHOW, never a value to parse. Read the\ndetail or the stream for the whole payload." type: string seq: description: "Seq is that event's position in the session's log \u2014 monotonic from 1, per\nsession. A reader\ \ holding it can ask the detail or stream reads for\neverything after it, so this doubles as the list's resume\ \ cursor." format: int64 type: integer type: object agent.metricsView: properties: range: description: echoes the requested window (24H|7D|30D) type: string resource: $ref: '#/components/schemas/agent.resourceUsage' description: "Resource is the Resource Usage panel's rollup, and every field of it is\ncurrently null \u2014 see\ \ resourceUsage. It is present rather than omitted so a\npanel renders \"\u2014\" instead of guessing." series: description: per-agent invocation histogram (real) items: $ref: '#/components/schemas/agent.seriesLine' type: array type: object agent.patchSessionIn: properties: cwd: description: "Cwd is where the session is working NOW.\n\nIt was write-once \u2014 captured at register and never\ \ again \u2014 which is right\nfor a run that starts in a directory and stays there, and wrong for a linked\n\ shell, which is a place a person moves around in. The console showed the\ndirectory `hanzo link` happened to be\ \ run from and kept showing it after the\nshell had walked away, so the field answered \"which work is this\"\ \ with an\nanswer that was true once. A pointer, so an unchanged path is an omitted\nfield rather than a repeated\ \ write." type: string id: description: ID is the session to update, from the path. type: string project: description: 'Project tags the product this session built; Published is the author''s decision to let anyone read the story (provenance.go). Both are pointers so "absent" and "cleared" are different requests.' type: string published: description: "Published opens the session's story to the public build route; false withdraws\nit, and withdrawing\ \ is always allowed. PUBLISHING is refused unless the\nsession names a Project \u2014 the one set in this same\ \ request, or the one already\nstored \u2014 because that route is keyed on (org, project). It widens READ access\n\ to what is already there and grants nothing else." type: boolean status: description: "Status moves the session to running, paused, done or error. A session that has\nalready finished refuses\ \ any change with 409 \u2014 done and error are monotonic \u2014\nand moving INTO one stamps the end time. This\ \ is the surface REPORTING what\nhappened; a control command never writes it." type: string target: description: 'Target re-dispatches a session to a run-target (the #48 association). "" detaches.' type: string terminal: description: 'Terminal publishes (or, with "", withdraws) the URL this session''s live terminal can be watched at. A pointer so "absent" and "withdrawn" are different requests: a session that stops sharing must be able to say so.' type: string title: description: "Title rewrites the human line, up to 512 characters \u2014 usually because the work\nturned out to\ \ be something other than what it was opened as." type: string type: object agent.patchTargetIn: properties: capacity: description: Capacity rewrites the human summary, up to 256 characters. "" clears it. type: string host: description: 'Host re-points the hostname sessions are matched by. Moving it moves the load: the session counts follow the new name from the next read.' type: string id: description: ID is the target to update, from the path. type: string kind: description: Kind re-files it under laptop | cloud | gpu | cluster | machine. type: string label: description: "Label renames the machine, up to 128 characters. Empty STRING is refused \u2014 a\ntarget with no\ \ name is a row nobody can pick out of a fleet." type: string metrics: $ref: '#/components/schemas/agent.Metrics' description: "Metrics replaces the live sample, and sending one IS A HEARTBEAT: the server\nstamps the time and\ \ appends the point to the fleet series. Sending an all-zero\nsample CLEARS the heartbeat \u2014 the machine goes\ \ back to having no liveness fact\nat all, and its stored status is taken at face value again." spec: $ref: '#/components/schemas/agent.Spec' description: 'Spec replaces the static capability whole, sanitized and clamped the same way a register''s is.' status: description: 'Status sets operator INTENT: online | offline | draining. Draining is how a machine is taken out of dispatch without ending what is already on it. What comes back may still read offline, because the heartbeat outranks the intent.' type: string type: object agent.registerReq: properties: account: description: 'Account is which subscription or API account under that provider served the run, up to 256 characters. It is what lets a revoke of that login stop exactly the sessions it was paying for.' type: string actor: description: 'Actor is the "org/sub" identity to record the session under, up to 256 characters. Omit it and the calling principal is used, which is almost always what you want: it is what a login revoke matches on to stop this session.' type: string agent: description: "Agent is the label the surface opening this session calls itself by\n(\"hanzo-dev\"). REQUIRED, up\ \ to 128 characters, and free text \u2014 nothing\nresolves it against a defined agent." type: string budget_micro_usd: description: 'BudgetMicroUSD caps what this session may spend, integer micro-USD. Zero means no cap; a cap can be raised or removed later, never added.' format: int64 type: integer cwd: description: 'Cwd is the directory the session starts in, up to 1024 characters. It can be moved later, because a linked shell walks around.' type: string host: description: "Execution context \u2014 where this session runs (all optional)." type: string parentSessionId: description: "ParentSessionID makes this a subagent of that session: it inherits the\nparent's root, so one flow\ \ stays one tree. The parent must exist IN THE SAME\nORG \u2014 a foreign or unknown id is a 400, never a tree\ \ across tenants. Empty\nopens a root session." type: string project: description: 'The readable build (provenance.go): which product this session builds, and whether its story may be read by the world.' type: string provider: description: "Account tag \u2014 the linked AI account this session ran under (login manager)." type: string published: description: "Published opens this session's story to the public build route. It is refused\nwithout a Project,\ \ because that route is keyed on (org, project) \u2014 a build\nwith no product is not a story anyone can open.\ \ False keeps it org-only." type: boolean repo: description: 'Repo is the code being worked on, up to 512 characters. A label the surface states; nothing resolves it against the forge.' type: string room: description: 'Room is the collaborative room this run was started in (HIP-0523), so a space view can list the sessions of one room. It is PROVENANCE and is set only here: there is deliberately no way to move a session to another room, so it is absent from the patch input and from UpdateSession''s SET list.' type: string status: description: "Status opens the session in one of running, paused, done or error. Empty means\nrunning. A TERMINAL\ \ status here (done, error) records a session that has\nalready finished \u2014 its end time is stamped now \u2014\ \ and nothing can move it\nafterwards." type: string target: description: 'Target names a run-target the org has registered. Unlike Host and Repo it IS resolved: a target that does not exist in this org is a 400, so a session can never claim to run on another tenant''s machine. Empty names no machine.' type: string taskRunId: description: 'TaskRunID is that workflow''s particular run, same bound. Recorded, not resolved: this surface does not check the workflow exists.' type: string taskWorkflowId: description: 'TaskWorkflowID links this session to the hanzoai/tasks workflow that executes it, up to 256 characters. Set it and control commands are forwarded to that engine; leave it and the running surface polls for them instead.' type: string terminal: description: "Terminal is the URL this session's live terminal is published at, so the\nconsole can watch it. Optional\ \ \u2014 a session that publishes nothing is still\na session." type: string title: description: 'Title is the human line a card shows, up to 512 characters. Optional, and changeable later.' type: string type: object agent.reportOut: properties: delivered: description: "Delivered is true when a waiting durable owner received this result. False\nmeans there was none to\ \ deliver to \u2014 an unknown or already-finished run \u2014 which\nis a clean no-op, not an error." type: boolean type: object agent.reportRunIn: properties: branch: description: 'Branch, CommitSha and Diffstat describe what the run produced; Error is the failure when OK is false. Each is clamped, never rejected.' type: string changed: description: 'Changed says whether the run produced any commit. It is INDEPENDENT of OK: a run can succeed and change nothing (there was nothing to do), and a run can fail after committing some of its work. Two questions, two booleans.' type: boolean commitSha: description: 'CommitSha is the tip the run pushed, clamped to 128 characters. Empty when it pushed nothing, which is the same case Changed reports false for.' type: string diffstat: description: 'Diffstat is the run''s own summary of what it changed, as text, clamped to 64 KiB. Free-form: it is shown, never parsed.' type: string error: description: "Error is why the run failed, clamped to 64 KiB. It is CLAMPED rather than\nrefused \u2014 a truncated\ \ reason is worth more than a rejected report, because a\nrejected report leaves the durable workflow waiting\ \ forever." type: string id: description: ID is the machine reporting, from the path. type: string ok: description: OK is whether the run succeeded; Changed whether it produced any commit. type: boolean runId: description: RunID is the routed run being completed, from the path. type: string type: object agent.resourceUsage: properties: costCents: description: "CostCents would be the window's spend in cents. Always null here \u2014 the money\na run costs is\ \ the metering ledger's, joined by the run id, and repeating it\nfrom this side would be a second number that\ \ could disagree with the bill." format: double type: number cpuVcpuHours: description: 'CPUVcpuHours would be vCPU-hours over the window. Always null: this store holds agent definitions and run I/O, and nothing here meters a CPU. Null is the honest answer and 0 would be a claim.' format: double type: number memGbHours: description: MemGbHours would be gigabyte-hours of memory. Always null, same reason. format: double type: number storageIoBytes: description: 'StorageIoBytes would be bytes moved to and from storage. Always null, same reason.' format: double type: number type: object agent.routedRunOut: properties: base: description: "Base is the branch to start FROM. Empty means the repository's default \u2014\nresolve it on the machine,\ \ since the machine is the one holding the clone." type: string branch: description: 'Branch is the ref the run must push its work to, and the ONLY one it is permitted to write: the forge''s ref policy refuses anything else from this run''s credential. It is decided at dispatch and exists before the work does.' type: string cloneUrl: description: "CloneURL is how to fetch the repository. It carries NO credential \u2014 the\nmachine authenticates\ \ with the git identity it already holds \u2014 which is why\nthis whole shape is safe to hand to a claimed runner." type: string effort: description: 'Effort is how hard that model reasons: low, medium or high. Empty is the model''s own default.' type: string instructions: description: 'Instructions are the person''s own instructions for their runs, as they set them in their settings: the machine''s harness reads them as its user''s, the way AGENTS.md or CLAUDE.md in its home is read. Empty when they set none.' type: string model: description: 'Model is the model the person asked the run''s agent to think with, by the id GET /v1/models lists. Empty is the machine''s own default.' type: string project: description: 'Project is the product slug the run is filed under, so the machine can tag what it produces. Empty when the dispatch named none.' type: string prompt: description: 'Prompt is the task, in full, as the person wrote it. There is no second field for context.' type: string repo: description: Repo is the repository to work in and CloneURL is how to fetch it. type: string sessionId: description: 'SessionID is the live session opened at dispatch; the machine streams its turns into it.' type: string timeoutSeconds: description: TimeoutSeconds bounds the run on the machine; 0 means the machine's own default. format: int64 type: integer type: object agent.runList: properties: runs: description: Runs is the agent's executions, newest first. items: $ref: '#/components/schemas/agent.agentRunView' type: array type: object agent.seriesLine: properties: key: description: agent name type: string points: description: "Points is one bucket per interval across the whole window, in time order and\nnever sparse: a bucket\ \ with no runs is present with v 0, so two lines drawn\nfrom two agents share an x-axis without the client aligning\ \ anything. The\nwindow decides the count \u2014 24 hourly for 24H, 7 daily, 30 daily." items: $ref: '#/components/schemas/agent.seriesPoint' type: array type: object agent.seriesPoint: properties: t: description: bucket start, RFC3339 UTC type: string v: description: real invocation count in the bucket format: int64 type: integer type: object agent.sessionBudgetIn: properties: budget_micro_usd: description: BudgetMicroUSD is the new cap, or null to remove the cap for good. format: int64 type: integer id: description: ID is the session, from the path. type: string type: object agent.sessionBudgetView: properties: budget_micro_usd: description: 'BudgetMicroUSD is this session''s own ceiling in micro-USD, beyond the agent''s. A replacement must strictly exceed what the session has already consumed, and removing it is one-way.' format: int64 type: integer budget_removed: description: BudgetRemoved says the session's own cap was taken off. It cannot be put back. type: boolean consumed_micro_usd: description: 'ConsumedMicroUSD is what has been spent in the current period, in micro-USD. It is settled from what the gateway reported, not from the quote.' format: int64 type: integer id: description: ID names the session. type: string status: description: 'Status is the session''s state: running, paused at its cap, or done.' type: string type: object agent.sessionDetail: properties: account: type: string actor: type: string agent: type: string base: type: string branch: type: string childSessions: description: 'Children is the session''s DIRECT children, one level down, each with its own counts. The promoted `children` integer beside it is how many there are; this is who they are. For the whole subtree, read the tree.' items: $ref: '#/components/schemas/agent.sessionView' type: array children: format: int64 type: integer createdAt: type: string cwd: type: string endedAt: type: string environment: type: string events: format: int64 type: integer host: type: string id: type: string kind: type: string lastEvent: $ref: '#/components/schemas/agent.lastEventView' mode: type: string org: type: string parentSessionId: type: string pr: type: string progress: $ref: '#/components/schemas/agent.sessionProgress' project: type: string provider: type: string published: type: boolean recentEvents: description: "RecentEvents is the 50 most recent turns, OLDEST of those first \u2014 a transcript\nto read down,\ \ not a feed. The promoted `events` integer says how many the log\nholds in total; page the rest from a seq." items: $ref: '#/components/schemas/agent.eventView' type: array repo: type: string room: type: string rootSessionId: type: string sandbox: type: string startedAt: type: string status: type: string target: type: string taskRunId: type: string taskWorkflowId: type: string terminal: type: string title: type: string updatedAt: type: string type: object agent.sessionList: properties: next: description: Next is the cursor for the page after this one; empty on the last page. type: string sessions: description: 'Sessions is the matching sessions, each with its event and child counts and a one-line preview of its latest event.' items: $ref: '#/components/schemas/agent.sessionView' type: array type: object agent.sessionProgress: properties: activity: description: 'Activity is the one line saying what the run is doing right now ("running the reaper''s tests"), up to 120 characters, in the model''s words. Empty when nothing has estimated it yet.' type: string at: description: 'At is when this was determined, RFC 3339 in UTC to the second. Read it as the estimate''s AGE: an estimate is not refreshed while nothing has happened, and a stale one beside a run that is still moving is itself worth seeing. Empty when nothing has estimated it yet.' type: string estimated: description: "Estimated says a MODEL produced this, from the run's transcript, and it may\nbe wrong. False means\ \ the session's own row said it: a finished run is 100%\nbecause it finished, not because anything guessed. Never\ \ treat a true here\nas a measurement \u2014 it is the reason to look, not the answer." type: boolean pct: description: "Pct is how much of the run is done, 0 to 100. THE KEY IS ABSENT when\nprogress is indeterminate \u2014\ \ a run nobody can estimate is not a run that has\ndone nothing, and rendering the second for the first is the\ \ mistake this\nomission exists to make impossible. Read `phase` before reaching for it." format: int64 type: integer phase: description: "Phase is what shape the run is in: running, blocked, done, error, or\nunknown when nothing has estimated\ \ it yet. blocked means the transcript\nshows the run waiting on something \u2014 an approval, a credential, an\ \ answer \u2014\nwhich is the one state the running surface cannot report about itself.\nerror only ever comes\ \ from the session's own terminal status." type: string type: object agent.sessionView: properties: account: description: 'Account is which subscription or API account under that provider served it. Together with Provider it is what a login revoke matches on to stop the sessions a withdrawn account was paying for.' type: string actor: description: "Actor is WHO this session belongs to, as \"org/sub\" \u2014 the same identity a run\nis billed under.\ \ A register that names none takes the calling principal. It is\nwhat scopes a login revoke, so a session with\ \ the wrong actor is a session the\nright person cannot stop." type: string agent: description: 'Agent is the label the surface running this session calls itself by ("hanzo-dev"), up to 128 characters. Required at register. It is free text, not a reference: it need not name a defined agent, and nothing resolves it.' type: string base: description: 'Base is the branch the run started from. Empty when it took the repository''s default and did not say which.' type: string branch: description: 'Branch is the branch the run pushes its work to, and the only one it may write.' type: string children: description: "Children is the DIRECT fan-out \u2014 how many sessions name this one as parent \u2014\nand not the\ \ size of the subtree. Read the tree for that." format: int64 type: integer createdAt: description: 'CreatedAt is when the row was written, same format. Every path that opens a session stamps it and StartedAt from one clock reading, so the two are equal on every session this surface has ever produced.' type: string cwd: description: 'Cwd is the directory the session is working in NOW, not the one it started in: a linked shell moves around, and a card showing where `hanzo link` was run answers "which work is this" with something that was true once.' type: string endedAt: description: 'EndedAt is when it reached done or error, same format. Empty while it is still running or paused, which is how absence reads here: not over yet.' type: string environment: description: 'Environment is where the run executes: "sandbox" for a Hanzo sandbox, or the id of the org''s machine it was routed to (the same id Target carries).' type: string events: description: 'Events is how many turns the session''s log holds, counted at read time. It is the whole log, however few of them RecentEvents carries.' format: int64 type: integer host: description: 'Execution context (mission-control): the machine/repo/cwd a card shows and the run-target a session is dispatched to. Omitted when a surface didn''t report it.' type: string id: description: 'ID is the session''s handle, minted here as "sess_" + 32 hex characters. Every later read, patch, event append and control command is addressed with it, and a caller cannot choose it.' type: string kind: description: "Kind is what sort of run this session is the view of \u2014 \"coding\" for a coding\nrun. Empty for\ \ a session no run tagged." type: string lastEvent: $ref: '#/components/schemas/agent.lastEventView' description: 'LastEvent is the compact latest-activity line for the list projection (nil in register/patch/tree responses; set by list + detail). It lets a swipe card show a live one-line preview without fetching full detail.' mode: description: 'Mode is what the run may do to its repository: "build" edits, commits and pushes its branch; "plan" reads and answers with a plan, writing nothing. Empty for a session no run tagged.' type: string org: description: "Org is the caller's OWN tenant, echoed so a client can build the public\nbuild URL (/builds/:org/:project)\ \ without a second call or a guess. It is\nnever another tenant's \u2014 every read is org-scoped before it gets\ \ here." type: string parentSessionId: description: "ParentSessionID is the session that spawned this one, making this a subagent\nof it. Empty means this\ \ session is a root \u2014 a flow of its own. A parent always\nbelongs to the same org, so a tree never crosses\ \ a tenant." type: string pr: description: 'PR is the address of the pull request the run proposed. Empty until it proposes one, and for a run that changed nothing.' type: string progress: $ref: '#/components/schemas/agent.sessionProgress' description: "Progress is how far along this run is \u2014 a share of its goal, a phase, and a\nline saying what\ \ it is doing. Always present, so a board never branches on\nwhether it is there; `phase` says \"unknown\" when\ \ nothing has estimated it.\nIt is a MODEL ESTIMATE wherever `estimated` is true, and the row's own word\nwhere\ \ it is false. See progress.go." project: description: 'The readable build: the product this session built and whether its story is public (provenance.go).' type: string provider: description: "Provider is the linked AI account's provider (claude | codex | hanzo | \u2026) that\nserved this run.\ \ Empty when the surface did not say." type: string published: description: 'Published is the author''s decision to let anyone read this session''s story at the public build route. It only ever widens READ access to a session that already exists and grants nothing else; false, an unpublished session is invisible there no matter who asks. It cannot be true without a Project, because that route is keyed on (org, project).' type: boolean repo: description: 'Repo is the code the session is working on, as the surface reported it. It is truth the SURFACE states, so it is a label rather than something resolved here.' type: string room: description: "Room is the collaborative room this run was started in (HIP-0523), empty\nwhen it came from anywhere\ \ else \u2014 a CLI, a schedule, an API call. It is what\nlets a space view show the runs of one room beside its\ \ messages." type: string rootSessionId: description: 'RootSessionID is the top of this session''s tree, inherited from the parent and shared by every node in one flow. A root session''s own id, when it has no parent. It is the key one indexed read pulls a whole flow by, and what ?root= narrows a list or a stream to.' type: string sandbox: description: "Sandbox is the id of the sandbox the run works in (\"m_\" and 24 hex), the\nhandle its terminal and\ \ its screen are opened with\n(/v1/sandbox/{id}/terminal, /screen). Set when the run leases it, and kept\nonce\ \ the run gives it back, when the id opens nothing. Empty for a run that\nnever leased one \u2014 a routed run,\ \ or a turn that only talked." type: string startedAt: description: StartedAt is when the session opened, RFC 3339 in UTC to the second. type: string status: description: "Status is one of exactly four: running, paused, done, error. running and\npaused are LIVE; done and\ \ error are TERMINAL and monotonic \u2014 once a session\nreaches one it can never go back, because reopening\ \ a finished run would\nfabricate liveness. A control command never moves it: the surface running the\nagent reports\ \ the new status, and until it does the command is only recorded." type: string target: description: "Target is the registered run-target this session is dispatched to \u2014 a machine\nthe org claimed,\ \ resolved same-org when it was set, so it can never point at\nanother tenant's computer. Empty means the session\ \ names no machine." type: string taskRunId: description: 'TaskRunID is that workflow''s particular run. A workflow is the definition and a run is one execution of it, which is why both are carried.' type: string taskWorkflowId: description: "TaskWorkflowID is the hanzoai/tasks durable workflow that actually EXECUTES\nthis session \u2014 this\ \ registry is the view, control and stream layer over it.\nSet, a control command is FORWARDED to that engine;\ \ empty, the running surface\npolls for commands instead, which is every session today." type: string terminal: description: "Terminal is where this session can be WATCHED \u2014 the URL the machine\npublished for its live terminal.\ \ Omitted when it publishes none." type: string title: description: 'Title is the human line a card shows ("ship the landing page"), up to 512 characters. Free text, and the one field a surface may rewrite as the work turns out to be something else.' type: string updatedAt: description: "UpdatedAt is the session's last-activity clock, same format. It moves on a\nwrite to the row \u2014\ \ a status, a title, a re-dispatch \u2014 AND on every appended\nturn, because the append bumps it in the same\ \ transaction. The list is ordered\non CreatedAt, so this is the field that says whether a session is still saying\n\ anything." type: string type: object agent.spendView: properties: by_component: additionalProperties: format: int64 type: integer description: ByComponent holds only the components with spend. Absent, not zero. type: object cap_micro_usd: description: 'CapMicroUSD is the total this agent may spend within one Period, as an integer number of micro-USD (1,000,000 = $1). It is required at creation: a cap of zero would mean no limit and no per-agent spend record at all.' format: int64 type: integer consumed_micro_usd: description: 'ConsumedMicroUSD is what has been spent in the current period, in micro-USD. It is settled from what the gateway reported, not from the quote.' format: int64 type: integer max_task_micro_usd: description: 'MaxTaskMicroUSD is the ceiling for a single run, in micro-USD. A session cannot exceed it even when the period cap still has room, so one runaway task cannot consume a month.' format: int64 type: integer period: description: 'Period is the window the cap resets on: day, week or month.' type: string period_started_at: description: PeriodStartedAt is when the current period began, as a Unix second. format: int64 type: integer ref: description: Ref names the agent this spend belongs to. type: string remaining_micro_usd: description: RemainingMicroUSD is what the cap still allows this period, in micro-USD. format: int64 type: integer type: object agent.targetDeleted: properties: deleted: description: Deleted is true when the target was removed. type: boolean id: description: ID is the target that was removed. type: string type: object agent.targetList: properties: targets: description: Targets is every target registered to the caller's org. items: $ref: '#/components/schemas/agent.targetView' type: array type: object agent.targetReq: properties: capacity: description: 'Capacity is a human summary of the machine''s size, up to 256 characters. Prose only; a scheduler reads Spec.' type: string host: description: 'Host is the hostname sessions on this machine will report. It is what makes a re-link IDEMPOTENT: the same (org, host, owner) refreshes the existing row and answers 200, while a request with no host always creates a new target and answers 201. It never adopts a row owned by somebody else.' type: string kind: description: 'Kind is laptop | cloud | gpu | cluster | machine. Empty registers a `machine`; anything outside the five is a 400.' type: string label: description: "Label is the name to show for this machine, up to 128 characters. REQUIRED \u2014\nit is the only\ \ field here a person reads." type: string metrics: $ref: '#/components/schemas/agent.Metrics' description: "Metrics is a live sample, and sending one IS A HEARTBEAT: it refreshes the row\nand starts the 90-second\ \ liveness window, and it is appended to the fleet\nseries as one point. Its own `at` is ignored \u2014 the server\ \ stamps the time, so\na client can never age or backdate its own machine. Omit it to register a\nmachine without\ \ claiming it is alive." spec: $ref: '#/components/schemas/agent.Spec' description: "Spec is the machine's static capability \u2014 os, arch, cores, RAM, accelerators.\nEvery field is\ \ bounded on write and at most 32 accelerators are accepted, so\nwhat comes back may be clamped. Omit it for a\ \ destination nothing probes." status: description: "Status is online | offline | draining. Empty registers `online`. It states\nINTENT \u2014 a heartbeat\ \ is what decides whether an online machine is actually\nreachable, so declaring online does not make it so." type: string type: object agent.targetView: properties: capacity: description: "Capacity is a human summary of what the machine has (\"8 vCPU / 32G\", \"1\xD7 GB10\"),\nup to 256\ \ characters. Prose for a card \u2014 Spec is the same thing in a form a\nscheduler can read, and nothing derives\ \ one from the other." type: string createdAt: description: 'CreatedAt is when the machine was first registered, RFC 3339 in UTC. A re-link refreshes the row and leaves this alone, so it dates the machine and not the connection.' type: string host: description: 'Host is the hostname sessions on this machine report, and it is a JOIN KEY, not a label: a session naming this host counts against the load below even when it names no target id, and a re-link of the same (org, host, owner) refreshes this row instead of creating a second. Empty means the machine is addressable only by ID.' type: string id: description: 'ID is the machine''s handle, minted as "tgt_" + 32 hex characters. It is what a session records to say it ran here, and what every later patch, claim or delete addresses.' type: string kind: description: 'Kind is what sort of destination this is, from a closed five: laptop | cloud | gpu | cluster | machine. A register that named none is a `machine`.' type: string label: description: 'Label is the name a person gave the machine ("workshop"), up to 128 characters. Required at register, free text, and the only field here meant for reading rather than matching.' type: string metrics: $ref: '#/components/schemas/agent.Metrics' description: "Metrics is what the machine was DOING at its last heartbeat \u2014 loadavg, memory,\naccelerator utilization.\ \ Absent when it has never beaten. It is a SNAPSHOT:\nthe series over time lives in the fleet samples, not here." metricsAt: description: "MetricsAt is when that heartbeat was recorded, RFC 3339 in UTC, and the SERVER\nstamps it \u2014 a\ \ client cannot backdate or forge the staleness clock. Absent means\nnever beaten, which is exactly the case where\ \ Status is taken at its word." type: string running: description: "Running is how many of those are in `running` right now \u2014 the number a\ndispatcher weighs against\ \ Capacity. paused sessions are in Sessions and not\nhere." format: int64 type: integer sessions: description: 'Sessions is how many of the org''s sessions are mapped to this machine, by target id OR by matching Host. All of them, whatever their status.' format: int64 type: integer spec: $ref: '#/components/schemas/agent.Spec' description: "Spec is what the machine IS \u2014 os, arch, cores, RAM, accelerators \u2014 the static\nhalf, changed\ \ only when something reports it again. Absent when nothing has\never been reported, and a scheduler reads absence\ \ as \"cannot satisfy a floor\"\nrather than as \"no limits\"." status: description: "Status is the EFFECTIVE liveness \u2014 online | offline | draining \u2014 not the\nstored one. offline\ \ and draining are operator INTENT and are reported as they\nstand; `online` is checked against the heartbeat,\ \ and a machine that has beaten\nbefore but not in the last 90 seconds reports offline whatever its row says.\ \ A\ntarget that has NEVER beaten keeps its stored status, because a hand-registered\ndestination has no fact\ \ to check." type: string updatedAt: description: "UpdatedAt is the last write to the row, same format \u2014 which for a beating\nmachine is its last\ \ heartbeat, since a heartbeat IS a write." type: string type: object agent.treeNode: properties: children: description: 'Children is this node''s direct children, each a whole node, so the array nests to the depth of the flow. A leaf carries null rather than an empty array. The subtree is materialised in full, up to 10000 nodes, out of one indexed read of the root; nothing is walked node by node.' items: $ref: '#/components/schemas/agent.treeNode' type: array session: $ref: '#/components/schemas/agent.sessionView' description: 'Session is this node''s own session, carrying its event count and its direct fan-out. It is the same shape the list and detail reads answer with, minus the last-event preview, which the tree does not fetch.' type: object agent.updateAgentIn: properties: avatar: description: "Avatar and Emoji re-draw the agent. Sending either replaces the pair, so\nsetting an image clears\ \ a glyph and \"\" for both goes back to the initial \u2014\nthere is no state where a row holds two answers." type: string cap_micro_usd: description: The budget, any part of it. Changing the period opens a new window. format: int64 type: integer computeRef: description: ComputeRef re-binds (or, with "", unbinds) the visor machine. Opaque here. type: string description: description: Description replaces the line other agents read in the tool catalogue. type: string emoji: description: "Emoji re-draws the agent as a glyph. Sending either of the pair replaces\nBOTH, so setting a glyph\ \ clears an image and \"\" for both goes back to the\ninitial \u2014 there is no state where a row holds two answers." type: string executionMode: description: 'ExecutionMode switches between one-shot and long-running. The RESULTING mode+schedule are validated together, so switching to long-running without a stored or supplied cron is refused rather than accepted into an agent the scheduler would skip forever. A switch INTO long-running counts against the per-org cap and can be a 409.' type: string instructions: description: 'Instructions replaces the system prompt whole, up to 32 KiB. There is no append: a prompt is one text, and sending "" clears it.' type: string max_task_micro_usd: description: 'MaxTaskMicroUSD is the ceiling for a single run, in micro-USD. A session cannot exceed it even when the period cap still has room, so one runaway task cannot consume a month.' format: int64 type: integer model: description: "Model re-points the agent at another model, checked against the gateway's\nserved catalogue exactly\ \ as create checks it. Empty STRING is refused \u2014 say\nnothing to keep the current one. Past runs keep the\ \ model that served them." type: string period: description: 'Period is the window the cap resets on: day, week or month.' type: string ref: description: "Ref is the agent to update \u2014 its public id or org-unique name, from the path." type: string schedule: description: 'Schedule replaces the cron. It is validated against the mode this update leaves behind, and dropped if that mode is one-shot.' type: string serviceAccountId: description: 'ServiceAccountID re-points (or, with "", clears) the IAM service account a scheduled run is billed as. Clearing it puts that spend back on the org.' type: string tools: description: 'Tools replaces the whole allow-list, it does not add to it. Sending [] takes every tool away, which is the only way to say that.' items: type: string type: array type: object ai.AiConnResponse: properties: account_label: type: string connected: type: boolean provider: type: string updated_at: type: string type: object ai.AnthropicContentBlock: properties: text: type: string type: type: string type: object ai.AnthropicMessage: properties: content: {} role: type: string type: object ai.AnthropicRequest: properties: max_tokens: type: integer messages: items: $ref: '#/components/schemas/ai.AnthropicMessage' type: array model: type: string stream: type: boolean system: {} temperature: type: number thinking: {} tool_choice: {} tools: items: $ref: '#/components/schemas/ai.AnthropicTool' type: array type: object ai.AnthropicResponse: properties: content: items: $ref: '#/components/schemas/ai.AnthropicContentBlock' type: array id: type: string model: type: string role: type: string stop_reason: type: string type: type: string usage: $ref: '#/components/schemas/ai.AnthropicUsage' type: object ai.AnthropicTool: properties: description: type: string input_schema: {} name: type: string type: object ai.AnthropicUsage: properties: input_tokens: type: integer output_tokens: type: integer type: object ai.CostStats: properties: baseline_model: type: string counterfactual_index: type: number cumulative_saved_index: type: number priced_events: type: integer routed_index: type: number saved_pct: type: number type: object ai.DecisionsAction: properties: act_probability: type: number type: object ai.DecisionsAnswer: properties: action: $ref: '#/components/schemas/ai.DecisionsAction' answer_confidence: type: number choice: type: string confidence: type: number legend: additionalProperties: {} type: object noul: type: number probabilities: additionalProperties: type: number type: object score: type: number type: type: string type: object ai.DecisionsQuestion: properties: criteria: {} instructions: type: string labels: additionalProperties: type: string type: object type: type: string type: object ai.DecisionsRequest: properties: model: type: string provider: {} questions: additionalProperties: $ref: '#/components/schemas/ai.DecisionsQuestion' type: object session_id: type: string state: {} trace: {} user: type: string type: object ai.DecisionsResponse: properties: answers: additionalProperties: $ref: '#/components/schemas/ai.DecisionsAnswer' type: object id: type: string latency_ms: type: number model: type: string provider: type: string routing: $ref: '#/components/schemas/ai.DecisionsRouting' state_hash: type: string usage: $ref: '#/components/schemas/ai.DecisionsUsage' type: object ai.DecisionsRouting: properties: backend: type: string calibration: type: string checkpoint: type: string device: type: string reason: type: string revision: type: string sha256: type: string upstream: type: string type: object ai.DecisionsUsage: properties: cost: type: number input_tokens: type: integer output_tokens: type: integer type: object ai.HistoryDay: properties: by_task: additionalProperties: type: integer type: object cost_saved_index: type: number cumulative_cost_saved: type: number date: type: string events: type: integer learned_share: type: number reward_rate: type: number rewarded_events: type: integer type: object ai.HistoryRetrain: properties: date: type: string events: type: integer gate_base: type: number gate_metric: type: string gate_pass: type: boolean gate_value: type: number holdout_accuracy: type: number published: type: boolean trained_time: type: string version: type: string type: object ai.HistoryTotals: properties: cumulative_cost_saved: type: number days_active: type: integer events: type: integer reward_rate: type: number type: object ai.HistoryWindow: properties: days: type: integer since: type: string until: type: string type: object ai.JudgeBenchmark: properties: mfjp: type: number naiveMean: type: number singleAdversary: type: number singleNoisy: type: number type: object ai.JudgePanelState: properties: available: type: boolean benchmark: $ref: '#/components/schemas/ai.JudgeBenchmark' enabled: type: boolean judges: items: $ref: '#/components/schemas/ai.PanelJudge' type: array models: items: type: string type: array sampleRate: type: number type: object ai.ModelAccessInfo: properties: state: type: string type: object ai.ModelInfo: properties: access: $ref: '#/components/schemas/ai.ModelAccessInfo' context_window: type: integer created: type: integer id: type: string max_output_tokens: type: integer object: type: string outputs: items: type: string type: array owned_by: type: string premium: type: boolean pricing: $ref: '#/components/schemas/ai.ModelPricingInfo' provider: type: string supports_tools: type: boolean supports_vision: type: boolean type: object ai.ModelList: properties: data: items: $ref: '#/components/schemas/ai.ModelInfo' type: array models: items: $ref: '#/components/schemas/ai.ModelInfo' type: array object: type: string type: object ai.ModelPricingInfo: properties: input: type: number output: type: number type: object ai.ModelProviders: properties: providers: items: type: string type: array type: object ai.PanelJudge: properties: mean: type: number model: type: string n: type: integer weight: type: number type: object ai.ProviderUsage: properties: available: type: boolean byModel: items: $ref: '#/components/schemas/ai.ProviderUsageModelSpend' type: array connected: type: boolean currency: type: string end: type: string interval: type: string note: type: string provider: type: string series: items: $ref: '#/components/schemas/ai.ProviderUsageSeriesPoint' type: array start: type: string totals: $ref: '#/components/schemas/ai.ProviderUsageTotals' type: object ai.ProviderUsageModelSpend: properties: model: type: string requests: type: integer spendCents: type: integer tokens: type: integer type: object ai.ProviderUsageSeriesPoint: properties: requests: type: integer spendCents: type: integer t: type: string tokens: type: integer type: object ai.ProviderUsageTotals: properties: inputTokens: type: integer outputTokens: type: integer requests: type: integer spendCents: type: integer tokens: type: integer type: object ai.QualityStats: properties: avg_confidence: type: number learned_share: type: number reward_rate: type: number rewarded_events: type: integer shadow_agreement: type: number type: object ai.RankUsage: properties: total_tokens: type: integer type: object ai.RankedDoc: properties: document: $ref: '#/components/schemas/ai.RankedText' index: type: integer relevance_score: type: number type: object ai.RankedText: properties: text: type: string type: object ai.Ranking: properties: model: type: string object: type: string results: items: $ref: '#/components/schemas/ai.RankedDoc' type: array usage: $ref: '#/components/schemas/ai.RankUsage' type: object ai.Response: properties: code: type: string data: {} data2: {} msg: type: string status: type: string type: object ai.ResponsesInputDetails: properties: cached_tokens: type: integer type: object ai.ResponsesOutputDetails: properties: reasoning_tokens: type: integer type: object ai.ResponsesResource: properties: created_at: type: integer error: {} id: type: string incomplete_details: {} instructions: type: string max_output_tokens: type: integer metadata: additionalProperties: type: string type: object model: type: string object: type: string output: items: {} type: array parallel_tool_calls: type: boolean previous_response_id: {} reasoning: {} status: type: string store: type: boolean temperature: type: number text: {} tool_choice: {} tools: items: $ref: '#/components/schemas/ai.ResponsesTool' type: array top_p: type: number usage: $ref: '#/components/schemas/ai.ResponsesUsage' type: object ai.ResponsesTool: properties: description: type: string format: {} name: type: string parameters: {} strict: type: boolean type: type: string type: object ai.ResponsesUsage: properties: input_tokens: type: integer input_tokens_details: $ref: '#/components/schemas/ai.ResponsesInputDetails' output_tokens: type: integer output_tokens_details: $ref: '#/components/schemas/ai.ResponsesOutputDetails' total_tokens: type: integer type: object ai.RetrainMeta: properties: events: type: integer gate_base: type: number gate_kind: type: string gate_metric: type: string gate_passed: type: boolean gate_value: type: number note: type: string published: type: boolean trained_time: type: string version: type: string type: object ai.RouterHistory: properties: daily: items: $ref: '#/components/schemas/ai.HistoryDay' type: array retrains: items: $ref: '#/components/schemas/ai.HistoryRetrain' type: array scope: type: string totals: $ref: '#/components/schemas/ai.HistoryTotals' window: $ref: '#/components/schemas/ai.HistoryWindow' type: object ai.RouterStats: properties: by_model: additionalProperties: type: integer type: object by_source: additionalProperties: type: integer type: object by_task: additionalProperties: $ref: '#/components/schemas/ai.TaskStats' type: object cost: $ref: '#/components/schemas/ai.CostStats' org: type: string quality: $ref: '#/components/schemas/ai.QualityStats' retrain: $ref: '#/components/schemas/ai.RetrainMeta' scope: type: string throughput: $ref: '#/components/schemas/ai.ThroughputStats' window: $ref: '#/components/schemas/ai.StatsWindow' type: object ai.RoutingRewardResult: properties: recorded: type: boolean request_id: type: string reward: type: number type: object ai.StatsWindow: properties: events: type: integer since: type: string until: type: string type: object ai.TaskStats: properties: events: type: integer models: additionalProperties: type: integer type: object type: object ai.ThroughputStats: properties: per_hour: items: type: integer type: array total_window: type: integer type: object ai.TokenCount: properties: input_tokens: type: integer type: object ai.VideoFailure: properties: message: type: string type: object ai.VideoStatus: properties: created_at: type: integer error: $ref: '#/components/schemas/ai.VideoFailure' id: type: string model: type: string object: type: string progress: type: integer status: type: string type: object aiLimit: properties: limit_cents: description: LimitCents is what the plan covers in this window, in billed cents. format: int64 type: integer resets_at: description: 'ResetsAt is when the window starts again (RFC3339), null for a session that is not running: it starts at the member''s next priced call.' type: string used_cents: description: UsedCents is what the plan has covered in this window, in billed cents. format: int64 type: integer type: object aiLimits: properties: month: $ref: '#/components/schemas/aiLimit' period_end: type: string period_start: description: PeriodStart and PeriodEnd bound the billing period (RFC3339). type: string plan: description: 'Plan is the rung that covers AI ("dev", "max-5x", "max-20x", "team", "team-annual"), empty when the caller''s plan covers none and they spend bought credit only.' type: string reset_available: description: 'ResetAvailable says the member can restart their session and weekly windows now; ResetExpiresAt is when an unused reset lapses (the period end).' type: boolean reset_expires_at: type: string session: $ref: '#/components/schemas/aiLimit' description: 'Session, Weekly, WeeklyPremium and Month are the four windows. Month is the org''s; the others are this member''s.' weekly: $ref: '#/components/schemas/aiLimit' weekly_premium: $ref: '#/components/schemas/aiLimit' type: object aiMCPApp: properties: name: description: Name is the subsystem, as the manifest names it. type: string served: description: 'Served reports that THIS process mounted it, so its tools are on this process''s MCP server rather than behind a sibling this process only knows the name of.' type: boolean type: object aiMCPSurface: properties: apps: description: Apps is one row per subsystem this deployment composes, in manifest order. items: $ref: '#/components/schemas/aiMCPApp' type: array names: description: 'Names are this process''s own tool names, present only when the query asked for them.' items: type: string type: array tools: description: "Tools is how many tools THIS PROCESS's MCP server carries: its own typed-op\nregistry, projected.\ \ It is the only number a subsystem can state honestly \u2014\nwhat the FLEET's server carries is a question only\ \ the host can ask, and it\nasks it by asking every subsystem (POST /v1/mcp, tools/list)." format: int64 type: integer type: object allowance.Allowance: properties: limit: description: calls the plan allows per period; 0 = unbounded format: int64 type: integer plan: description: the tier the limit came from type: string resets: description: unix seconds; when THAT window starts again format: int64 type: integer spent: description: the subject is at the limit type: boolean used: description: "Used is how many zero-priced calls this subject has been SERVED in the period\nending at Resets \u2014\ \ the UTC calendar day. Only a served call counts, so an\nadmission check, a refusal, or a vendor that never answered\ \ leaves it where it\nstood. It stops AT Limit rather than climbing past it, so Limit-Used is what\nremains and\ \ never goes negative." format: int64 type: integer window: description: "Window is which ceiling these numbers describe \u2014 \"hour\" or \"day\" \u2014 because a\ncaller\ \ is held to both and only one of them is the answer. It is the window\nthat REFUSED where one did, and otherwise\ \ the one with least left, so\nLimit-Used is always the number that will actually stop them next. Empty\nwhere\ \ no window bounds the subject at all." type: string type: object ask.Report: properties: answer: description: 'Answer is the grounded prose, with inline markdown citations. Every link in it points at a page in Sources: the citation check runs on the text before it leaves the engine, so a cited URL is one THIS call fetched.' type: string follow_ups: description: "FollowUps are the questions worth asking next. Best-effort \u2014 an empty list\nis a normal outcome,\ \ not a fault." items: type: string type: array mode: description: 'Mode is the profile that ran: search, news, research or deep.' type: string model: description: Model is the model that synthesized the answer. type: string sources: description: 'Sources are the pages the answer was written from, deduplicated and ranked. Always an array, never null.' items: $ref: '#/components/schemas/ask.Source' type: array type: object ask.Source: properties: engine: description: 'Engine is the search backend the hit came from: bing, ddg, mojeek or brave. Omitted when the backend did not name itself. Results are merged across backends, so two sources in one answer can carry different engines.' type: string favicon: description: "Favicon is a 64px icon URL derived from the host for the client to render\nbeside the citation. It\ \ is Google's s2 service, not something we host or\nfetched \u2014 an empty host yields the empty string." type: string snippet: description: 'Snippet is the engine''s summary of the page, clipped to 600 runes. THIS IS WHAT THE CLIENT SHOWS. What the model reads is the fetched page, which is far larger and deliberately never on the wire.' type: string title: description: 'Title is the page title the engine reported, stripped of the bracketed furniture engines staple on ("[PDF]", "(Official Site)"). It falls back to the www-stripped host when the engine gave none, so it is never empty and is safe to use as link text.' type: string url: description: "URL is the page, absolute, exactly as the engine gave it. It is also the\ndedupe key \u2014 one source\ \ per URL, and at most hostCap per host \u2014 and what a\nmarkdown citation in the answer is checked against,\ \ so a link in the prose\nalways matches a URL here." type: string type: object ask.webQuestion: properties: language: description: 'Language narrows the search to a locale, BCP-47-ish ("en", "ja"). Empty means no narrowing.' type: string max_sources: description: MaxSources caps how many pages are read. Empty means the mode's own budget. format: int64 type: integer mode: description: 'Mode is how much work to do: `search` (fast, one pass), `news` (recency biased), `research` (a plan and several rounds) or `deep` (the widest survey). Empty means research.' type: string q: description: Q is the question, in plain language. Required. type: string sources: description: 'Sources narrows where the evidence comes from: any of `web`, `news`, `academic`, `github`, `reddit`, `x`. Each becomes a site-scoped search, so `["x"]` researches X/Twitter posts rather than the open web.' items: type: string type: array type: object askRequest: properties: followUps: type: boolean language: type: string maxQueries: type: integer maxSources: type: integer mode: type: string model: type: string q: type: string question: type: string sources: items: type: string type: array stream: type: boolean system: type: string type: object audit.Wire: properties: action: description: "Action is the verb that was performed. It is the event's name, not the HTTP\nmethod \u2014 a request-sourced\ \ record carries both, and the pair is what makes a\nrow readable (\"grant.create\" at POST /v1/admin/grants)." type: string authMethod: description: 'Auth is the credential the actor presented: "jwt", "api-key", or "none".' type: string email: description: 'Email is the actor''s validated address, absent when the credential carried none. It comes from the verified token, never from a client header.' type: string hash: description: 'Hash is this record''s SHA-256 over its own canonical JSON with both hash fields cleared, folded with prevHash. Recomputing it from the row''s other fields is what proves the row has not been edited.' type: string home: description: 'Home is present ONLY on a cross-org action: the org the actor came FROM, while Org is the org they acted IN. A console row carrying `home` is a platform-admin impersonation and should be rendered as one.' type: string isAdmin: description: 'IsAdmin is the VALIDATED platform-SuperAdmin bit at decision time (membership of the reserved admin org), never the client''s own claim to be one.' type: boolean method: description: 'Method is the HTTP verb, on a record a request produced. Absent on an event emitted from inside the binary with no request behind it.' type: string org: description: "Org is the tenant the action was taken IN \u2014 the effective org, which for\neveryone but an impersonating\ \ SuperAdmin is also the actor's own. Empty on an\nunauthenticated request." type: string path: description: 'Path is the request''s route. Any segment shaped like a credential is replaced before the record is written, so a key that rides in a path is not preserved here by the very control meant to watch it.' type: string prevHash: description: 'PrevHash is the hash of record seq-1, which is what links the rows into a chain: a deleted or reordered record breaks the recomputation at that point. The first record of a chain carries 64 zeros rather than an empty string, so "start of chain" and "field missing" cannot look alike.' type: string reason: description: 'Reason is a short explanation for a deny or an error ("SuperAdmin required", "insufficient_balance"). It is never a secret and never a raw upstream error body; absent on a success.' type: string requestId: description: "RequestID ties this row to the request-line log and any downstream trace \u2014 the\nX-Request-Id\ \ the pipeline minted for that request." type: string resource: description: "Resource is the KIND of thing acted upon (\"org\", \"role\", \"secret\",\n\"provider-config\", \"\ credit\"). Where a mutation has no finer semantics than its\nroute, this is the route family and resourceId is\ \ empty \u2014 the action and the\npath already pin the object." type: string resourceId: description: ResourceID is the specific instance, absent when the kind alone identifies it. type: string result: description: 'Result is how the action ended: "success", "deny" or "error". A deny is a decision this binary made and is as much evidence as a success.' type: string seq: description: 'Seq is the record''s position in the chain, 0-based and gapless. The Recorder assigns it under its own lock, so it is a true total order: seq n+1 was written after seq n, and a missing number is a missing record.' format: uint64 type: integer sourceIp: description: "SourceIP is the client address the edge resolved for the request, after the\nproxy chain \u2014 the\ \ address a responder would act on, not the socket peer." type: string status: description: 'Status is the HTTP status the caller received. It is the outcome as the client saw it, so a 200 carrying a domain refusal still reads 200 here.' format: int64 type: integer sub: description: 'Sub is the acting user (the IAM subject). Empty for a machine principal or an anonymous request, which is how a service action is told from a person''s.' type: string time: description: 'Time is when the action happened, RFC3339Nano in UTC. The stored column has the same precision and sorts the same way, so a client can range and order on this string verbatim.' type: string userAgent: description: 'UserAgent is the client the request announced itself as. Client-supplied, so it is evidence about what claimed to act, not proof of it.' type: string type: object audit.trailPage: properties: data: description: Data is one page of the org's events, newest first. Empty, never null. items: $ref: '#/components/schemas/audit.Wire' type: array msg: description: Msg is the envelope's message slot, empty on success. type: string status: description: Status is the envelope's status slot, "ok" on success. type: string total: description: "Total is how many events match the filter, across all pages \u2014 what a pager\nneeds to size itself." format: int64 type: integer type: object author.authorRepo: properties: badgeMarkdown: description: 'BadgeMarkdown is the ready-to-paste README snippet, DERIVED for each response from this deployment''s badge host and never stored: a "Deploy on Hanzo" image linking to the one-click import of this repository. Re-hosting the builder changes every badge without touching a row.' type: string createdAt: description: 'CreatedAt is unix seconds when the claim was first recorded. It equals verifiedAt on the first proof and then stays put while verifiedAt moves, so the pair reads as "claimed since / last proven".' format: int64 type: integer method: description: "Method is HOW ownership was proven: \"oauth\" \u2014 an IAM-linked forge token showed\nadmin or push\ \ on the repository; \"file\" \u2014 a hanzo.json on the default branch\ncarried this author's verify code; or\ \ \"maintainer\" \u2014 the repository sits in a\nfirst-party namespace, where ownership is intrinsic and the\ \ treasury author\nholds it with no proof step. Omitted on a row written before the method was\nrecorded." type: string repoUrl: description: "RepoURL is the claim key in canonical form \u2014 lowercased \"host/owner/name\",\nno scheme, no .git,\ \ host \u2208 {github.com, gitlab.com}. A deploy's source repo is\nnormalized through the same function before\ \ attribution, so the two sides can\nnever miss on a cosmetic difference. UNIQUE across every author: first proven\n\ claim wins." type: string verified: description: 'Verified reports that ownership was proven. Only a proven claim is ever written, so it is true on every row this surface returns; the deploy path re-reads it regardless, because an unverified claim attributes nothing.' type: boolean verifiedAt: description: 'VerifiedAt is unix seconds of the most recent successful proof. Re-verifying refreshes it, and the method beside it, in place.' format: int64 type: integer type: object author.claim: properties: created: description: 'Created reports whether this call recorded a new claim (201) or found an existing one (200).' type: boolean org: $ref: '#/components/schemas/author.orgView' description: 'Org is the verified owner-wide claim, present when an owner was claimed. It covers every repository the author publishes under that owner.' repo: $ref: '#/components/schemas/author.authorRepo' description: Repo is the verified repository claim, present when a repository was claimed. type: object author.connectRequest: properties: githubLogin: description: "GithubLogin is the account to link. Used only when IAM holds no linked\naccount for the provider \u2014\ \ a linked account is stronger proof and always wins." type: string login: description: 'Login is the provider-neutral alias for GithubLogin, preferred when both are sent.' type: string provider: description: 'Provider is the forge to enrol with: github (the default) or gitlab.' type: string type: object author.deployRecord: properties: created: description: 'Created reports whether this call recorded a new attribution edge (201) or found an existing one (200). Absent when nothing was recorded.' type: boolean createdAt: description: 'CreatedAt is when the edge was first recorded, in unix seconds. Absent when nothing was recorded.' format: int64 type: integer deployId: description: DeployID is the attribution edge's handle. Absent when nothing was recorded. type: string reason: description: Reason says why nothing was attributed. Present only when recorded is false. type: string recorded: description: "Recorded reports whether the deploy was attributed to an author at all. False\nis the ordinary answer\ \ for a project built from no repository, or from one no\nauthor has verified \u2014 never an error, so a deploy\ \ path can fire this\nunconditionally." type: boolean self: description: 'Self reports that the deploying org IS the author''s org. Such a deploy is recorded for provenance but excluded from accrual. Absent when nothing was recorded.' type: boolean type: object author.deployRequest: properties: project: description: Project is the deployed project's id. Required. type: string repoUrl: description: "RepoURL is the source repository the project was built from. Empty means a\nhand-built project with\ \ nothing to attribute \u2014 an honest no-op, not an error." type: string type: object author.enrolment: properties: created: description: 'Created reports whether this call enrolled the org (201) or found an existing enrolment (200).' type: boolean githubLogin: description: GithubLogin is the linked forge account. type: string id: description: ID is the author record's server-minted handle, "aut_"-prefixed. type: string shareBps: description: 'ShareBps is this author''s royalty share in basis points of the spend their deployed work generates.' format: int64 type: integer status: description: Status is connected, approved or suspended. Only an approved author earns. type: string verified: description: Verified reports whether any repository or owner claim has been proven yet. type: boolean verifyCode: description: "VerifyCode is this author's stable proof token \u2014 the value a repository's\nverify file must carry." type: string verifyFile: description: VerifyFile is the repo-root file the file method reads, on the default branch. type: string verifySnippet: description: VerifySnippet is that file's exact contents, ready to commit. type: string type: object author.orgView: properties: badgeMarkdown: description: "BadgeMarkdown is the ready-to-paste README snippet, DERIVED for each response\nfrom this deployment's\ \ badge host and never stored \u2014 here it deep-links the\nOWNER's template import rather than one repository's." type: string createdAt: description: "CreatedAt is unix seconds when the owner claim was first recorded \u2014 equal to\nverifiedAt on the\ \ first proof, then fixed while verifiedAt moves." format: int64 type: integer method: description: "Method is HOW the owner was proven, always against its \".github\" control\nrepository: \"oauth\"\ \ \u2014 an IAM-linked forge token showed admin or push on it; or\n\"file\" \u2014 a hanzo.json on its default\ \ branch carried this author's verify code.\nThe \"maintainer\" shortcut is a per-repository attribution and never\ \ appears\nhere. Omitted on a row written before the method was recorded." type: string ownerUrl: description: "OwnerURL is the claim key in canonical form \u2014 lowercased \"host/owner\" with NO\nrepository segment,\ \ host \u2208 {github.com, gitlab.com}. It covers every repository\nunder that owner, so code with no claim of\ \ its own still earns; a per-repository\nclaim outranks it. UNIQUE across every author: first proven claim wins." type: string verified: description: "Verified reports that ownership of the WHOLE owner was proven \u2014 against that\nowner's \".github\"\ \ control repository, which is exactly as strong as a\nper-repository claim. Only a proven claim is written, so\ \ every row returned\nhere is true." type: boolean verifiedAt: description: 'VerifiedAt is unix seconds of the most recent successful proof of the owner; re-verifying refreshes it, and the method beside it, in place.' format: int64 type: integer type: object author.verifyRequest: properties: repoUrl: description: 'RepoURL is what to claim: a repository (github.com/owner/name) or a whole OWNER (github.com/owner, no repository segment). gitlab.com is accepted too.' type: string type: object auto.Catalog: properties: connectorCount: format: int64 type: integer connectors: items: $ref: '#/components/schemas/auto.ConnectorMetadata' type: array runnableCount: description: 'RunnableCount is how many of them this deployment can actually invoke. It is the number that matters and it was absent: a caller read connectorCount and reasonably believed all of it worked.' format: int64 type: integer type: object auto.ConnectorAction: properties: description: type: string displayName: type: string name: type: string props: items: $ref: '#/components/schemas/auto.PropSpec' type: array type: object auto.ConnectorAuth: properties: required: type: boolean type: type: string type: object auto.ConnectorMetadata: properties: actions: items: $ref: '#/components/schemas/auto.ConnectorAction' type: array auth: $ref: '#/components/schemas/auto.ConnectorAuth' categories: items: type: string type: array description: type: string displayName: type: string logoUrl: type: string name: type: string runnable: description: "Runnable is whether this connector's actions run HERE, so a caller can\ninvoke what the entry lists.\ \ It is the registry's answer \u2014 an action\nregistered for the connector \u2014 never a stored flag, because\ \ a stored flag\nis a second answer to a question the registry already answers." type: boolean triggers: items: $ref: '#/components/schemas/auto.ConnectorTrigger' type: array version: type: string type: object auto.ConnectorTrigger: properties: description: type: string displayName: type: string name: type: string props: items: $ref: '#/components/schemas/auto.PropSpec' type: array strategy: type: string type: object auto.Flow: properties: created: format: int64 type: integer externalId: type: string folderId: type: string id: type: string metadata: {} projectId: description: projectId == org (server-derived) type: string publishedVersionId: type: string status: type: string updated: format: int64 type: integer type: object auto.FlowAction: properties: displayName: type: string name: type: string nextAction: $ref: '#/components/schemas/auto.FlowAction' settings: $ref: '#/components/schemas/auto.StepSettings' skip: type: boolean type: description: PIECE | CODE | ROUTER | LOOP_ON_ITEMS type: string valid: type: boolean type: object auto.FlowRun: properties: created: format: int64 type: integer finishTime: format: int64 type: integer flowId: type: string flowVersionId: type: string id: type: string startTime: format: int64 type: integer status: type: string updated: format: int64 type: integer type: object auto.FlowTrigger: properties: displayName: type: string name: type: string nextAction: $ref: '#/components/schemas/auto.FlowAction' settings: $ref: '#/components/schemas/auto.StepSettings' strategy: type: string type: description: PIECE_TRIGGER | EMPTY type: string valid: type: boolean type: object auto.FlowVersion: properties: created: format: int64 type: integer displayName: type: string flowId: type: string id: type: string schemaVersion: type: string state: type: string trigger: $ref: '#/components/schemas/auto.FlowTrigger' updated: format: int64 type: integer valid: type: boolean type: object auto.PropSpec: properties: description: description: Description says what the value is. type: string displayName: description: DisplayName is what a form calls it. type: string name: description: Name is the input's key in the action's input object. type: string required: description: Required is whether the action refuses to run without it. type: boolean type: description: 'Type is the JSON type the value takes: string|number|boolean|object|array.' type: string type: object auto.StepSettings: properties: actionName: type: string input: additionalProperties: {} type: object pieceName: type: string pieceVersion: type: string triggerName: type: string type: object auto.createFlowReq: properties: displayName: description: DisplayName names the flow's initial draft version. type: string externalId: description: ExternalID is the caller's own id for this flow. Optional. type: string folderId: description: FolderID groups the flow in the builder's tree. Optional. type: string trigger: $ref: '#/components/schemas/auto.FlowTrigger' description: "Trigger is the root of the step tree \u2014 how the flow starts, and the action\nchain that follows.\ \ Optional: a flow may be created empty and edited later." type: object auto.createVersionIn: properties: displayName: description: DisplayName names the new version. type: string id: description: ID is the flow to add a version to, from the path. type: string trigger: $ref: '#/components/schemas/auto.FlowTrigger' description: 'Trigger is the root of the version''s step tree. Optional: a version with no trigger is created invalid, and cannot run until one is set.' type: object auto.flowPage: properties: data: description: Data is the page of flows, newest-updated first. items: $ref: '#/components/schemas/auto.Flow' type: array type: object auto.patchFlowIn: properties: externalId: description: ExternalID sets the caller's own id for this flow. type: string folderId: description: FolderID moves the flow in the builder's tree. type: string id: description: ID is the flow to update, from the path. type: string metadata: description: Metadata replaces the caller's opaque JSON. publishedVersionId: description: 'PublishedVersionID pins the version runs execute. It must name a version OF THIS FLOW; empty clears the pin, so runs take the latest version again.' type: string type: object auto.populatedFlow: properties: created: description: Created and Updated are unix milliseconds. format: int64 type: integer externalId: description: ExternalID is the caller's own id for this flow, if it set one. type: string folderId: description: FolderID groups the flow in the builder's tree. type: string id: description: ID is the flow's id. type: string metadata: description: Metadata is the caller's opaque JSON, stored and returned verbatim. projectId: description: "Org is the owning org, which this surface names projectId. Server-derived from\nthe validated principal\ \ \u2014 never read from a request." type: string publishedVersionId: description: 'PublishedVersionID is the version a run executes when set; empty means the latest version runs.' type: string status: description: "Status is ENABLED or DISABLED \u2014 whether the flow's trigger is armed." type: string updated: format: int64 type: integer version: $ref: '#/components/schemas/auto.FlowVersion' description: "Version is the flow's latest version \u2014 its display name and step tree." type: object auto.runIn: properties: action: description: Action is the name of the provider action to invoke. type: string auth: description: 'Auth is the caller''s resolved credential for the provider, handed to the action verbatim. Its shape is whatever the provider''s auth descriptor declares (a token string, an object), so it is opaque here.' id: description: ID is the provider to run, from the path. type: string props: additionalProperties: {} description: Props are the action's input properties, keyed by property name. type: object type: object auto.runPage: properties: data: description: Data is the page of runs, newest first. items: $ref: '#/components/schemas/auto.FlowRun' type: array type: object auto.runResp: properties: error: description: Error is the provider-level failure message when not ok. type: string ok: description: Ok reports whether the action ran to completion. type: boolean output: description: Output is the action's result when ok. Its shape is the action's own. type: object auto.versionPage: properties: data: description: Data is the page of versions, newest first. items: $ref: '#/components/schemas/auto.FlowVersion' type: array type: object base.baseHealth: properties: service: description: "Service is \"base\" \u2014 which subsystem answered." type: string status: description: Status is "ok" when the subsystem is serving. type: string type: object base.baseView: properties: bytes: description: 'Bytes is the store''s size on disk, present only once it exists. It is what this Base occupies, not a quota.' format: int64 type: integer exists: description: 'Exists reports whether this Base''s store has been provisioned. False is an org nobody has stored anything for yet, which is a state to name rather than an error: the store is created the first time anything writes.' type: boolean org: description: 'Org is the org this Base belongs to. It is the address every other Base call is scoped by, and a Base has no name of its own.' type: string type: object benchmark.Benchmark: properties: axis: description: what capability it measures type: string id: description: the id every other op on this surface takes type: string items: description: how many items it holds, when the set is fixed format: int64 type: integer native: description: whether the standardized harness runs it today type: boolean source: description: where the items come from type: string title: description: the benchmark's published name type: string type: object benchmark.ClaimRow: properties: at: description: At is when a stored row was recorded. Zero for a seed row. format: date-time type: string benchmark: description: Benchmark is the canonical test id the claim is about, from /catalog. type: string by: description: By is who recorded it, when the caller said. type: string model: description: Model is the system the score is claimed for. type: string origin: description: 'Origin is "seed" for a compiled row and "stored" for one written through this surface. It is the difference between what we shipped and what an operator has since corrected.' type: string protocol: description: "Protocol records HOW it was scored \u2014 provider-reported, agentic,\nthird-party-leaderboard \u2014\ \ so a provider card is never read as a measurement." type: string provider: description: "Provider is who the claim belongs to \u2014 the lab or leaderboard whose number\nthis is." type: string score: description: Score is the reported aggregate, as a percentage. format: double type: number source: description: Source is the citation the row was read from. type: string type: object benchmark.LeaderRow: properties: ciHigh: description: "CIHigh is the upper bound of that interval. Wilson rather than the normal\napproximation because the\ \ normal one produces bounds past 100 exactly where\nbenchmark scores live \u2014 at 194/198 that is the top of\ \ the board, not a\ncorner case." format: double type: number ciLow: description: "CILow and CIHigh are the 95% Wilson interval on Measured, in percent. They\nare what makes the score\ \ comparable: at n=198 a 98% carries roughly \xB12\npoints, so most differences at the top of a board are not\ \ distinguishable\nand a bare number implies a precision it does not have. Absent when there\nis no measurement." format: double type: number claims: description: 'Claims is how many independent claims exist for this model on this benchmark. More than one means several sources reported it.' format: int64 type: integer gap: description: "published \u2212 measured (the arena signal)" format: double type: number mean: description: 'Mean is the unweighted average of every claim, which answers a different question from Published: what the field says on average, rather than what the vendor says about itself. With one claim the two are equal.' format: double type: number measured: description: hanzo-measured accuracy % (nil if unrun) format: double type: number measuredAt: description: MeasuredAt is when the run behind Measured was recorded. format: date-time type: string model: description: the model this row scores type: string n: description: "coverage \u2014 NEVER compare across different n" format: int64 type: integer protocol: description: 'how the vendor scored their claim: single-attempt, pass@k or agentic' type: string published: description: provider-claimed % (nil if none) format: double type: number run: description: "Run names the measurement Measured came from, and MeasuredAt is when it\nran. A score with no date\ \ is not a fact about a model, it is a fact about\na model on a day \u2014 and models change, so the date is what\ \ makes the number\ncheckable rather than merely quoted." type: string spread: description: "Spread is the distance between the highest and lowest of them, nil when\nthere is only one. It is\ \ the disagreement AMONG sources, which a single\nPublished number cannot show \u2014 signal in the same way the\n\ published-minus-measured gap is." format: double type: number type: object benchmark.ModelHistory: properties: model: description: Model is the system these runs measured. type: string points: description: Points is every run, oldest first. items: $ref: '#/components/schemas/benchmark.RunPoint' type: array trend: description: 'Trend is the change from the first run to the last, absent when there has only been one. It answers the question a list of points makes you compute.' format: double type: number type: object benchmark.Preset: properties: arms: description: "the blend \u2014 model ids from the arena" items: type: string type: array name: description: served as enso- type: string note: description: why this blend (audit) type: string owner: description: scoping org (never cross-tenant) type: string panel: description: fan-out width (>=1) format: int64 type: integer rank: description: escalation order over arms items: type: string type: array type: object benchmark.RunPoint: properties: at: description: At is when the run was recorded. format: date-time type: string delta: description: 'Delta is the change in score from the previous run for this model, absent on the first. It is the number the whole surface exists to make visible.' format: double type: number n: description: 'N is how many items the run covered. Two runs are only comparable at the same n, which is why it travels with every point rather than being assumed.' format: int64 type: integer run: description: Run is the measurement id these attempts were recorded under. type: string score: description: Score is accuracy over the items this run covered, as a percentage. format: double type: number type: object benchmark.admission: properties: benchmarks: description: Benchmarks are the catalog ids admitted. items: type: string type: array endpoint: description: Endpoint is the caller's own endpoint the run targets. type: string model: description: Model is the catalog model the run targets. type: string note: description: Note explains what admission does and does not promise. type: string status: description: 'Status is "queued": the run is admitted, not finished.' type: string type: object benchmark.benchmarkCatalog: properties: data: description: Data is one row per benchmark, in the catalog's own order. items: $ref: '#/components/schemas/benchmark.Benchmark' type: array total: description: Total is how many rows Data holds. format: int64 type: integer type: object benchmark.claimsOut: properties: data: description: "Data is one row per (benchmark, model, SOURCE) \u2014 every independent claim,\nnot one per model.\ \ Effective values only: the row that wins after layering\nfor each source, never the superseded readings behind\ \ it." items: $ref: '#/components/schemas/benchmark.ClaimRow' type: array total: description: Total is how many rows Data holds. format: int64 type: integer type: object benchmark.historyOut: properties: benchmark: description: Benchmark is the catalog id these histories are about. type: string data: description: Data is one entry per model, ordered by model name. items: $ref: '#/components/schemas/benchmark.ModelHistory' type: array total: description: Total is how many models Data holds. format: int64 type: integer type: object benchmark.leaderboard: properties: benchmark: description: Benchmark is the catalog id these rows are about. type: string rows: description: Rows is one per model, ordered by measured accuracy descending. items: $ref: '#/components/schemas/benchmark.LeaderRow' type: array type: object benchmark.pairing: properties: a: description: A is the first model id. type: string a_correct: description: ACorrect is how many of those common items A got right. format: int64 type: integer b: description: B is the second model id. type: string b_correct: description: BCorrect is how many of those common items B got right. format: int64 type: integer benchmark: description: Benchmark is the catalog id the two arms were compared on. type: string mcnemar_p: description: 'McnemarP is the two-sided exact binomial p on the discordant pairs. It is 1 when nothing is discordant, which is "no evidence of a difference", not an error.' format: double type: number n_common: description: 'NCommon is how many items BOTH arms completed. It is the denominator, and the reason this comparison is valid where a raw accuracy difference is not.' format: int64 type: integer net_a_minus_b: description: "NetAMinusB is the two rescue counts subtracted \u2014 A's advantage in items." format: int64 type: integer rescue_a_over_b: description: RescueAOverB is how many items A got right and B got wrong. format: int64 type: integer rescue_b_over_a: description: RescueBOverA is how many items B got right and A got wrong. format: int64 type: integer type: object benchmark.presetAccepted: properties: note: description: Note explains what acceptance does and does not promise. type: string preset: $ref: '#/components/schemas/benchmark.Preset' description: Preset is the blend with its defaults filled in. served_as: description: ServedAs is the model id the serving layer would resolve this blend under. type: string status: description: 'Status is "accepted": the blend is well-formed, not that it is now served.' type: string type: object benchmark.presetList: properties: data: description: Data is the blends available to compose from. items: $ref: '#/components/schemas/benchmark.Preset' type: array type: object benchmark.publishedClaim: properties: benchmark: description: Benchmark is the canonical test id the claim is about, from /catalog. type: string model: description: Model is the system the score is claimed for. type: string protocol: description: "Protocol records HOW it was scored \u2014 provider-reported, agentic,\nthird-party-leaderboard \u2014\ \ because a provider card and a third party running\nits own harness are different kinds of number and must not\ \ be blended." type: string provider: description: "Provider is who the claim belongs to \u2014 the lab or leaderboard whose number\nthis is. It joins\ \ a claim to the attempts measured for that same model." type: string score: description: Score is the reported aggregate, as a percentage. format: double type: number source: description: 'Source is the citation the row was read from. A claim without one is a number nobody can check, so every write requires it.' type: string type: object benchmark.putClaimsIn: properties: by: description: "By is who is recording them \u2014 a person, or the importer's name." type: string data: description: 'Data is the claims to record. One row is a correction; many is an import. There is no separate bulk endpoint because there is no separate operation: importing a leaderboard and fixing one number are the same write.' items: $ref: '#/components/schemas/benchmark.publishedClaim' type: array type: object benchmark.putClaimsOut: properties: recorded: description: Recorded is how many rows were written. format: int64 type: integer rejected: description: Rejected names the rows that were not, and why. items: type: string type: array type: object benchmark.suite: properties: attempts: description: 'Attempts is how many times to try each item; the harness''s default applies when it is omitted.' format: int64 type: integer benchmarks: description: 'Benchmarks are the catalog ids to run. At least one is required, and every id must be in the catalog.' items: type: string type: array endpoint: description: 'Endpoint is your own chat-completions URL, for benchmarking a model this arena does not host. Either this or model is required.' type: string model: description: Model is the catalog model id to run. Either this or endpoint is required. type: string required: - benchmarks type: object billing.AccountsTotal: properties: accounts: description: Accounts is how many linked accounts the total folds. format: int64 type: integer completionTokens: description: CompletionTokens is the total completion-token count. format: int64 type: integer costCents: description: CostCents is the total cost in cents. format: int64 type: integer promptTokens: description: PromptTokens is the total prompt-token count. format: int64 type: integer requests: description: Requests is the total request count the gateway routed. format: int64 type: integer totalTokens: description: TotalTokens is the total token count. format: int64 type: integer type: object billing.Alert: properties: createdAt: type: string currency: type: string enforce: type: boolean id: type: string over: type: boolean period: type: string periodSpentCents: format: int64 type: integer project: type: string rateLimitRpm: format: int64 type: integer resetsAt: type: string service: type: string softPct: format: int64 type: integer threshold: format: int64 type: integer title: type: string triggeredAt: type: string updatedAt: type: string userId: type: string warn: type: boolean type: object billing.AlertPatch: properties: enforce: type: boolean id: type: string project: type: string rateLimitRpm: format: int64 type: integer service: type: string softPct: format: int64 type: integer subject: type: string threshold: format: int64 type: integer title: type: string type: object billing.AlertSpec: properties: currency: type: string enforce: type: boolean project: type: string rateLimitRpm: format: int64 type: integer service: type: string softPct: format: int64 type: integer subject: type: string threshold: format: int64 type: integer title: type: string type: object billing.AutoRecharge: properties: amountCents: format: int64 type: integer currency: type: string enabled: type: boolean lastRechargedAt: type: string stored: type: boolean subject: type: string thresholdCents: format: int64 type: integer type: object billing.AutoRechargeEdit: properties: amountCents: format: int64 type: integer currency: type: string enabled: type: boolean thresholdCents: format: int64 type: integer type: object billing.BillingAccount: properties: createdAt: type: string currency: type: string id: type: string name: type: string orgId: type: string orgName: type: string role: description: 'Role is the caller''s standing. Absent when nobody was named, so an anonymous read returns an account rather than an implied membership.' type: string type: object billing.BillingInvoice: properties: amountDue: format: int64 type: integer amountPaid: format: int64 type: integer attemptCount: format: int64 type: integer createdAt: type: string creditApplied: format: int64 type: integer currency: type: string customerEmail: type: string discount: format: int64 type: integer dueDate: type: string id: type: string lineItems: description: 'LineItems carries no omitempty and is never allocated empty, because the wire it reproduces sends `null` for an invoice with no lines. An empty array there would be a different answer to "were there lines".' items: $ref: '#/components/schemas/billing.InvoiceLineItem' type: array number: format: int64 type: integer numberStr: type: string paidAt: type: string paymentMethod: type: string paymentRef: type: string periodEnd: type: string periodStart: type: string status: type: string subscriptionId: type: string subtotal: format: int64 type: integer tax: format: int64 type: integer updatedAt: type: string userId: type: string voidedAt: type: string type: object billing.CapVerdict: properties: allow: type: boolean capCents: format: int64 type: integer reason: type: string spentCents: format: int64 type: integer warnPct: format: int64 type: integer type: object billing.Charged: properties: balanceCents: description: 'BalanceCents is the subject''s balance AFTER the charge settled, in cents, so a caller does not have to re-read to show the new number.' format: int64 type: integer processorRef: description: 'ProcessorRef is the payment processor''s own reference. It is the only field that proves money moved at the GATEWAY rather than merely in our ledger, which is why it is answered and not only logged. Absent where the processor returned none.' type: string status: description: 'Status is how the charge ended. Read it rather than inferring success from the HTTP status: the call succeeded whenever this field is present, and what the PROCESSOR did is what this says.' type: string test: description: "Test states which bucket was credited \u2014 sandbox money or real money \u2014 so no\nreader has\ \ to guess whether a receipt is real. Sandbox and live funds are\nphysically separate ledgers, and a reader that\ \ conflates them restates the\ncompany's revenue." type: boolean transactionId: description: 'TransactionID is the ledger entry this charge created. It is the handle a later read or a refund names, and it is minted by the ledger rather than by the caller.' type: string type: object billing.Collected: properties: balanceUsedCents: description: BalanceUsedCents is how much was covered by prepaid balance. format: int64 type: integer cardChargedCents: description: CardChargedCents is how much was charged to the card on file. format: int64 type: integer creditUsedCents: description: CreditUsedCents is how much was covered by credit grants. format: int64 type: integer invoice: $ref: '#/components/schemas/billing.Invoice' description: "Invoice is the invoice AFTER the attempt \u2014 its status is the authority on\nwhat happened, not\ \ this struct's other fields." paid: description: 'Paid reports whether the invoice is now settled in full. A false here with no error is a DECLINE: the invoice stays open and may be collected again.' type: boolean processorRef: description: "ProcessorRef is the processor's reference for any card charge \u2014 the field\nthat proves money\ \ moved at the gateway rather than only in our ledger." type: string reason: description: Reason explains a decline or partial collection. Empty on success. type: string type: object billing.CreditBalance: properties: balances: items: $ref: '#/components/schemas/billing.CreditEntry' type: array userId: type: string type: object billing.CreditEntry: properties: available: format: int64 type: integer currency: type: string type: object billing.CreditGrant: properties: active: type: boolean amountCents: format: int64 type: integer createdAt: type: string currency: type: string effectiveAt: type: string expiresAt: type: string id: type: string name: type: string priority: format: int64 type: integer remainingCents: format: int64 type: integer tags: type: string userId: type: string voided: type: boolean type: object billing.CreditGrants: properties: count: format: int64 type: integer grants: items: $ref: '#/components/schemas/billing.CreditGrant' type: array type: object billing.CryptoDeposit: properties: addressTag: type: string chain: type: string depositAddress: type: string expiresAt: type: string id: type: string status: type: string token: type: string type: object billing.CryptoOptions: properties: chains: items: type: string type: array tokens: items: type: string type: array type: object billing.Detachment: properties: deleted: description: "Deleted is whether the method was actually removed. False with no error\nmeans it was already gone,\ \ which is a successful detach rather than a\nfailure \u2014 a retry must not be an error." type: boolean id: description: 'ID is the method that was detached, echoed so a caller batching several can tell the answers apart.' type: string type: object billing.Holder: properties: addedAt: type: string email: type: string id: type: string role: type: string userId: type: string type: object billing.Invoice: properties: amountDueCents: description: AmountDueCents is what remains collectible. format: int64 type: integer amountPaidCents: description: AmountPaidCents is what has been collected so far. format: int64 type: integer createdAt: description: CreatedAt is when the draft was raised, RFC3339. type: string currency: description: Currency is the ISO 4217 code. type: string customerEmail: description: CustomerEmail is where it is sent. type: string id: description: "ID is the invoice id \u2014 what the issue, collect and void ops address." type: string lines: description: Lines are the charges on the invoice. items: $ref: '#/components/schemas/billing.InvoiceLine' type: array number: description: 'Number is the human-facing invoice number, e.g. "INV-0042". A draft has none; issuing assigns it.' type: string paymentRef: description: PaymentRef is the processor reference for the collection, once paid. type: string status: description: 'Status is draft, open, paid, void or uncollectible. A draft is not collectible; issuing moves it to open.' type: string subtotalCents: description: SubtotalCents is the sum of the lines. format: int64 type: integer userId: description: UserID is the customer billed. type: string type: object billing.InvoiceLine: properties: amount: description: Amount is the line total in whole cents (250000 is $2,500.00). format: int64 type: integer description: description: "Description is the human-readable line, e.g. \"Advisory retainer \u2014 August\"." type: string quantity: description: Quantity is the number of units, when the line is metered. Optional. format: int64 type: integer unitPrice: description: UnitPrice is the per-unit price in cents, when the line is metered. Optional. format: int64 type: integer type: object billing.InvoiceLineItem: properties: amount: format: int64 type: integer currency: type: string description: type: string id: type: string meterId: type: string periodEnd: type: string periodStart: description: "The billed period. Both carry omitempty and neither is ever empty, which\nlooks contradictory and\ \ is not: the shape they reproduce is a time value,\nand omitempty does nothing to a struct \u2014 so those keys\ \ render even for the\nzero instant. The adapter formats the zero instant rather than skipping it,\nwhich is what\ \ keeps the two wires the same." type: string planId: type: string planName: type: string quantity: format: int64 type: integer type: type: string unitPrice: format: int64 type: integer type: object billing.Invoices: properties: count: format: int64 type: integer cursor: type: string invoices: items: $ref: '#/components/schemas/billing.BillingInvoice' type: array type: object billing.Mode: properties: live: type: boolean orgId: type: string orgName: type: string testMode: type: boolean type: object billing.ModeIn: properties: testMode: type: boolean type: object billing.Money: properties: currency: type: string decimal: type: string required: - decimal - currency type: object billing.PaymentConfig: properties: applicationId: type: string environment: type: string live: type: boolean locationId: type: string provider: type: string type: object billing.Payout: properties: amount: format: int64 type: integer arrivalDate: type: string created: type: string currency: type: string description: type: string destinationId: type: string destinationType: type: string failureCode: type: string failureMessage: type: string id: type: string metadata: {} providerRef: type: string status: type: string type: object billing.RaiseIn: properties: currency: description: Currency is the ISO 4217 code, lower-cased. Empty means usd. type: string customerEmail: description: CustomerEmail is where the invoice is sent. Optional. type: string lines: description: "Lines are the charges. The invoice subtotal and amount due are COMPUTED\nfrom these \u2014 there is\ \ no total field to send, because a total that\ndisagreed with its own lines would bill a number nobody could\ \ derive." items: $ref: '#/components/schemas/billing.InvoiceLine' type: array userId: description: "UserID identifies the customer being billed, within the caller's own org.\nRequired \u2014 an invoice\ \ with no addressee is not an invoice." type: string type: object billing.Recharge: properties: charged: description: 'Charged is how many of them were actually charged. It is at most Orgs, and the difference is orgs whose balance was already above their threshold.' format: int64 type: integer orgs: description: "Orgs is how many orgs the sweep considered \u2014 every org with auto-recharge\narmed, whether or\ \ not it needed charging." format: int64 type: integer results: description: 'Results is one row per org considered, so a sweep that charged nobody is still explainable. Never null.' items: $ref: '#/components/schemas/billing.Recharged' type: array type: object billing.Recharged: properties: amountCents: format: int64 type: integer balanceCents: format: int64 type: integer charged: type: boolean error: type: string orgName: type: string transactionId: type: string userId: type: string type: object billing.Rollup: properties: balance: $ref: '#/components/schemas/billing.RollupBalance' consumedCents: format: int64 type: integer currency: type: string included: $ref: '#/components/schemas/billing.RollupAllotment' overageCents: format: int64 type: integer period: type: string plan: type: string user: type: string windows: items: $ref: '#/components/schemas/billing.Window' type: array type: object billing.RollupAllotment: properties: consumedCents: format: int64 type: integer grantedCents: format: int64 type: integer monthlyCents: format: int64 type: integer remainingCents: format: int64 type: integer type: object billing.RollupBalance: properties: availableCents: format: int64 type: integer balanceCents: format: int64 type: integer holdsCents: format: int64 type: integer type: object billing.RoutedUsage: properties: account: description: Account is the provider-side account identifier. type: string billing: description: 'Billing is how the routed inference bills: plan or commerce.' type: string completionTokens: description: CompletionTokens is the routed completion-token count. format: int64 type: integer costCents: description: CostCents is the routed cost in cents. format: int64 type: integer kind: description: 'Kind is how the account authenticates: subscription or apikey.' type: string promptTokens: description: PromptTokens is the routed prompt-token count. format: int64 type: integer provider: description: Provider is the AI provider the row's account belongs to. type: string requests: description: Requests is how many requests the gateway routed through this account. format: int64 type: integer totalTokens: description: TotalTokens is the routed total token count. format: int64 type: integer type: object billing.Subscription: properties: cancelAtPeriodEnd: description: CancelAtPeriodEnd reports that the plan ends at CurrentPeriodEnd. type: boolean canceledAt: description: CanceledAt is when it was canceled; absent when it was not. type: string createdAt: description: CreatedAt is when the row was written, RFC 3339. type: string currentPeriodEnd: description: CurrentPeriodEnd is when it ends, RFC 3339. The plan is paid through it. type: string currentPeriodStart: description: CurrentPeriodStart is when the period in hand began, RFC 3339. type: string defaultPaymentMethod: description: 'DefaultPaymentMethod is the saved method a renewal would use. Empty means there is none, so nothing can charge the plan.' type: string endedAt: description: EndedAt is when it ended; absent while it runs. type: string id: description: ID is the subscription's own id, the one cancel and reactivate take. type: string mrrCents: description: "MRRCents is what this subscription contributes per month \u2014 commerce's own\nfigure, interval-normalized\ \ and multiplied by its seats, so no reader\nre-derives it from price and interval." format: int64 type: integer plan: $ref: '#/components/schemas/billing.SubscriptionPlan' description: Plan is the plan as this subscription snapshotted it. planId: description: PlanID is the plan slug, e.g. "max-20x". type: string providerType: description: 'ProviderType is who collects it: a processor ("square"), "credit" for a plan bought with credits, "internal" or "bundle" for a row nobody paid for.' type: string quantity: description: Quantity is the seat count; a flat plan holds 1. format: int64 type: integer status: description: 'Status is trialing, active, past_due, canceled or unpaid. Only active and trialing confer the plan.' type: string trialEnd: description: TrialEnd is when that trial ends; absent when there was none. type: string trialStart: description: TrialStart is when a trial began; absent when there was none. type: string updatedAt: description: UpdatedAt is when the row last changed, RFC 3339. type: string userId: description: 'UserID is the billing account that holds it: the org slug for an org''s own plan, "/" for a member''s.' type: string type: object billing.SubscriptionPlan: properties: currency: description: Currency is the ISO code the price is in, lower-cased. type: string id: description: ID is the plan row the subscription was opened on. type: string interval: description: 'Interval is the billing period: "month" or "year".' type: string name: description: Name is the plan's display name, e.g. "Max 20x". type: string price: description: 'Price is what one period costs per seat, in cents, as the subscription snapshotted it: a later catalog price change does not move it.' format: int64 type: integer type: object billing.SubscriptionRef: properties: atPeriodEnd: description: 'AtPeriodEnd cancels at the end of the paid period rather than at once. It defaults TRUE on the endpoint, because a customer who cancels has already paid for the period they are in.' type: boolean id: type: string type: object billing.Subscriptions: properties: count: description: 'Count is the row count beside the rows, which is the shape this address has always answered with.' format: int64 type: integer subscriptions: items: $ref: '#/components/schemas/billing.Subscription' type: array type: object billing.Tier: properties: balance: $ref: '#/components/schemas/billing.TierBalance' plan: description: 'Plan is the catalog rung the subject is served as ("" with no plan). A tier is a class of rungs; a bound that differs within one class keys on this.' type: string tier: $ref: '#/components/schemas/billing.TierLimits' user: type: string windows: items: $ref: '#/components/schemas/billing.Window' type: array type: object billing.TierBalance: properties: creditsRemaining: format: int64 type: integer currency: type: string dailyRemaining: format: int64 type: integer effectiveAvailable: format: int64 type: integer prepaidAvailable: format: int64 type: integer type: object billing.TierLimits: properties: allowedModels: items: type: string type: array dailyCreditsCents: format: int64 type: integer displayName: type: string maxAgents: format: int64 type: integer name: type: string unlimitedAgents: description: "UnlimitedAgents reports that MaxAgents 0 means \"no ceiling\" rather than\n\"no agents\" \u2014 the\ \ reading a bare zero cannot carry." type: boolean type: object billing.Transaction: properties: amount: format: int64 type: integer createdAt: type: string currency: type: string expiresAt: type: string id: type: string metadata: {} notes: type: string tags: type: string type: type: string type: object billing.Transactions: properties: count: format: int64 type: integer transactions: items: $ref: '#/components/schemas/billing.Transaction' type: array user: type: string type: object billing.Window: properties: limit: format: int64 type: integer remaining: format: int64 type: integer resets: type: string span: type: string used: format: int64 type: integer type: object billing.WireInstructions: properties: accountName: type: string accountNumber: type: string bankAddress: type: string bankName: type: string iban: type: string memo: type: string reference: type: string routingNumber: type: string swiftCode: type: string type: object billing.accounts: properties: accounts: items: $ref: '#/components/schemas/billing.RoutedUsage' type: array scope: type: string source: type: string total: $ref: '#/components/schemas/billing.AccountsTotal' type: object billing.cryptoAsset: properties: amountCents: description: "AmountCents is what the payer intends to send, for the record. Optional \u2014\nthe credit is what\ \ actually arrives, never what was announced." format: int64 type: integer chain: description: Chain is the network to receive on. Empty takes the rail's default. type: string token: description: Token is the asset on that chain. Empty takes the chain's native one. type: string type: object billing.financeLedgerEntry: properties: account: type: string balanceCents: format: int64 type: integer cents: format: int64 type: integer currency: type: string date: type: string description: type: string id: type: string type: object billing.topupIn: properties: amountCents: description: AmountCents is how much to charge, in cents of Currency. Required. format: int64 type: integer currency: description: 'Currency is the ISO-4217 code to charge in. Empty takes the deployment''s own default.' type: string paymentMethodId: description: MethodID names a card the subject already saved, for the saved-card endpoint. type: string sourceId: description: 'SourceID is a single-use card token from the payment form, for the token endpoint. It is vaulted as part of the charge, so a caller never holds card numbers and this service never sees one.' type: string type: object billing.usageReceipt: properties: account: description: 'Account is the wallet the debit drew from, the same key GET /v1/billing/balance reports for this caller.' type: string amount: $ref: '#/components/schemas/billing.Money' description: Amount is the debit, exactly as recorded. id: description: ID is the act's name, echoed. type: string org: description: Org is the organization whose ledger holds the debit. type: string type: object billing.usageReport: properties: amount: $ref: '#/components/schemas/billing.Money' description: 'Amount is what the act costs, as an exact decimal in USD. It must be greater than zero.' id: description: 'ID names the act, chosen by the reporting application and stable across its retries: the ledger debits one act once. At most 128 printable characters, no whitespace. Reusing an ID for a different amount is a 409.' type: string model: description: 'Model names the unit the amount prices (a machine size, a model id), and is recorded with the debit.' type: string org: description: "Org is the organization this usage is billed to. It must be the org the\ntoken acts in \u2014 the\ \ application's own, or one that granted it membership and\nis selected with X-Org-Id \u2014 and any other is\ \ refused before money moves." type: string project: description: 'Project attributes the debit to one project of the org. Empty is the org''s default project.' type: string service: description: 'Service scopes the debit for spend caps and attributes it to a product, as a lowercase slug. Empty takes the reporting application''s name.' type: string type: object blueprint.RateCard: properties: basis: description: 'Basis names where the rates come from, so a published price can be explained rather than merely asserted.' type: string microUsdPerGbHour: description: 'MicroUSDPerGBHour is the price of one GiB of memory for one hour, in millionths of a US dollar.' format: int64 type: integer microUsdPerVcpuHour: description: 'MicroUSDPerVCPUHour is the price of one vCPU for one hour, in millionths of a US dollar.' format: int64 type: integer type: object blueprint.blueprintHealth: properties: blueprints: description: Blueprints is how many blueprints this build has embedded and priced. format: int64 type: integer rateCard: $ref: '#/components/schemas/blueprint.RateCard' description: 'RateCard is the rate card actually in force after the operator env overlay (CLOUD_BLUEPRINT_UCPU_HR / CLOUD_BLUEPRINT_UGB_HR), not the shipped default.' service: description: "Service names the subsystem answering \u2014 always \"blueprint\"." type: string status: description: Status is "ok"; the route answers 200 whenever the subsystem is mounted. type: string type: object blueprint.blueprintIndex: properties: data: description: Data is one row per embedded blueprint, sorted by template id. items: $ref: '#/components/schemas/blueprint.blueprintRow' type: array type: object blueprint.blueprintRow: properties: estCentsPerMonth: description: 'CentsPerMonth is the estimated compute cost of running the whole stack for one month, in USD cents, from the rate card GET /v1/blueprint/health echoes.' format: int64 type: integer services: description: Services is how many compose services the stack runs. format: int64 type: integer templateId: description: "TemplateID is the blueprint slug \u2014 the id GET /v1/blueprint/sbom takes as\n?template= and the\ \ path under templates.hanzo.ai/blueprints//." type: string type: object books.Account: properties: name: description: Name is the account's human name, for a statement's line label. type: string number: description: "Number is the posting key every voucher leg, rule and report references \u2014\nstable, and the reason\ \ the chart is a fixed value rather than a table anybody\ncan edit. It looks numeric and is a string: \"1000\"\ \ sorts and compares as text." type: string party: description: "Party marks the account as carrying a SUBLEDGER \u2014 receivable is money owed to\nus, payable money\ \ we owe \u2014 so a leg posted here also writes a payment-ledger\nrow against a counterparty. Absent means no\ \ subledger: a bank, wallet, revenue\nor cost account tracks no counterparty at all." type: string type: description: 'Type is the account''s fundamental class, which is also its NORMAL balance side: asset and expense are debit-normal, liability, income and equity credit-normal.' type: string type: object books.AskRequest: properties: from: description: 'From is the RFC3339 start of the metric window. Empty means all time, treated as a single reporting period (see monthsBetween).' type: string question: description: 'Question is the plain-language question about the org''s books, e.g. "what is my MRR?". Longer than 2000 characters is truncated, never refused.' type: string to: description: To is the RFC3339 end of the metric window. Empty means up to now. type: string type: object books.AskResponse: properties: answer: description: 'Answer is one or two sentences answering the question, every number in it taken from Figures.' type: string figures: description: Figures are the grounded numbers the answer states, each already formatted. items: $ref: '#/components/schemas/books.Figure' type: array followups: description: Followups are sharper questions to ask next, chosen from the same intent. items: type: string type: array sources: description: "Sources name the books reports the figures were computed from \u2014 \"pnl\",\n\"position\", \"trial\"\ ." items: type: string type: array type: object books.BalanceLine: properties: account: description: "Account is the chart-of-accounts number this line reports on. ABSENT marks a\nDERIVED line that no\ \ account holds \u2014 retained earnings is the one such line,\ncomputed from cumulative income minus expense." type: string amount: description: 'Amount is the balance as of the statement date, in whole cents, in its NATURAL sign: positive when the account behaved normally, on all three sides. Assets are debit-normal and shown as stored; liabilities and equity are credit-normal and flipped once here for display. A negative asset is a real overdraft, not a sign convention.' format: int64 type: integer name: description: Name is the account's human name, or the derived line's own name. type: string type: description: 'Type is the account''s fundamental class. Absent on a derived line, which belongs to no account and therefore has none.' type: string type: object books.BalanceSheet: properties: asOf: description: 'AsOf is the posting time the statement is taken at, inclusive. A balance sheet is a snapshot, not a window, so there is no From. Absent means as of now.' type: string assets: description: 'Assets are what the org OWNS at that instant, one line per account that has a balance. Cash, receivables, funds captured but not yet settled.' items: $ref: '#/components/schemas/books.BalanceLine' type: array balanced: description: "Balanced is whether assets equal liabilities plus equity \u2014 the accounting\nequation, computed\ \ from the totals above rather than assumed. False means the\nledger is broken, not that the statement is." type: boolean equity: description: "Equity is what is left over for the owners. It carries a DERIVED retained\nearnings line holding cumulative\ \ income minus expense, because this ledger has\nno period close that sweeps the P&L into equity \u2014 without\ \ that line the\nequation would not close." items: $ref: '#/components/schemas/books.BalanceLine' type: array liabilities: description: "Liabilities are what the org OWES \u2014 including customers' unspent prepaid\ncredit, which is their\ \ money until it is consumed and so is carried here\nrather than counted as revenue." items: $ref: '#/components/schemas/books.BalanceLine' type: array totalAssets: description: TotalAssets is the sum of the asset lines, in cents. format: int64 type: integer totalEquity: description: TotalEquity is the sum of the equity lines including retained earnings, in cents. format: int64 type: integer totalLiabilities: description: TotalLiabilities is the sum of the liability lines, in cents. format: int64 type: integer type: object books.BankQuestion: properties: connector: description: 'Connector names the feed the unplaceable line arrived on. With externalId it identifies both the question and the bank line it is about, so re-syncing the same deposit never asks twice.' type: string createdAt: description: CreatedAt is when the question was raised. type: string externalId: description: ExternalID is the bank's own id for the line in question. type: string prompt: description: "Prompt is the question put to the founder in plain language \u2014 what this money\nwas, since the\ \ books cannot place it on their own." type: string status: description: Status is whether the question is still open or has been answered. type: string type: object books.BankTally: properties: ingested: description: transactions seen format: int64 type: integer posted: description: vouchers newly posted (outflow + reconciled) format: int64 type: integer questions: description: unmatched inflows that raised a question format: int64 type: integer reconciled: description: inflows cleared against Square-clearing format: int64 type: integer skipped: description: already-processed idempotent no-ops format: int64 type: integer transfers: description: own-account moves recorded (no P&L) format: int64 type: integer type: object books.BankTxnRow: properties: amountCents: description: "AmountCents is the size of the movement in whole cents, always POSITIVE \u2014\ndirection carries\ \ the sign, so a caller must read both to know which way money\nwent." format: int64 type: integer connector: description: "Connector names the feed this row arrived on \u2014 which bank or processor\nconnection it was synced\ \ from. With externalId it is the row's identity, so\nre-syncing the same statement never books a second copy." type: string currency: description: Currency is the ISO code the bank reported the line in. type: string description: description: Description is the statement memo as the bank wrote it. type: string direction: description: 'Direction is which way the money moved: an inflow into the account or an outflow from it, from the org''s point of view.' type: string externalId: description: 'ExternalID is the bank''s OWN id for the line, carried verbatim. It is unique only within its connector.' type: string matchedVoucher: description: "MatchedVoucher names the ledger voucher this line was reconciled against \u2014\nthe bill it paid,\ \ or the settlement it cleared. Absent when nothing matched,\nwhich for an inflow is what raises a question." type: string merchant: description: Merchant is the counterparty the feed identified, where it did. type: string postedAt: description: PostedAt is the bank's posting date for the line, not when we synced it. type: string status: description: 'Status is where the line got to: posted (an outflow booked straight to an expense), settled (an outflow that paid down a scanned bill), reconciled (an inflow that cleared a pending settlement), transfer (a move between the org''s own accounts, recorded but with no effect on the books), or unmatched (an inflow nobody could place, which is waiting on a human answer).' type: string type: object books.BookRequest: properties: override: description: "Override books this bill even when one of the SAME economic identity\n(vendor, total, issue date)\ \ already posted \u2014 the explicit human confirmation that a\nsame-looking bill is a genuine second spend, not\ \ the same receipt re-scanned." type: boolean scanId: description: 'ScanID is the scanned document''s file hash, as GET /v1/books/inbox and the scan draft report it. It is the idempotency key: re-booking the same scan writes nothing.' type: string voucher: $ref: '#/components/schemas/books.Voucher' description: 'Voucher is the reviewed voucher to post. Its source is FORCED to (scan, scanId) server-side, so it can never be booked under another source''s key.' type: object books.BookResponse: properties: posted: description: 'Posted is true when this call wrote the voucher, false when the same scan had already booked and nothing was written.' type: boolean scanId: description: ScanID echoes the scan that was booked. type: string type: object books.Extracted: properties: category: description: "Category is the expense bucket the SCANNER guessed, as a slug \u2014 a hint only.\nVendor rules override\ \ it whenever they know better, so this is the model's\nreading and not the account the entry will land on." type: string currency: description: Currency is the ISO code the document is denominated in. type: string issuedAt: description: "IssuedAt is the document's OWN date as YYYY-MM-DD \u2014 when the bill was issued,\nwhich is not when\ \ it was uploaded or when it will post." type: string lineItems: description: 'LineItems are the individual lines read off the document, where it had any. They need not sum to totalCents: a document may carry lines the scanner could not read, and the total is taken from the total.' items: $ref: '#/components/schemas/books.LineItem' type: array merchant: description: Merchant is the supplier as printed on the document. type: string note: description: 'Note is anything else worth carrying from the document that has no field of its own.' type: string taxCents: description: 'TaxCents is how much of that total is tax, in cents. It is part of totalCents, not additional to it.' format: int64 type: integer totalCents: description: TotalCents is the document total in whole cents, tax INCLUDED. format: int64 type: integer type: object books.Figure: properties: label: description: Label names the metric, e.g. "MRR" or "Runway". type: string period: description: Period is the window the figure covers, e.g. "2026-07" or "all-time". type: string value: description: 'Value is the figure already formatted through books'' own money formatter, so a consumer never re-derives it.' type: string type: object books.FinancialPackage: properties: balanceSheet: $ref: '#/components/schemas/books.BalanceSheet' description: BalanceSheet is struck as of the period END, not the start. from: description: From opens the reporting period. Absent means from the beginning of the ledger. type: string generatedAt: description: "GeneratedAt is when the bundle was assembled \u2014 the moment the statements were\nstruck, which\ \ is what makes two exports of the same period comparable." type: string gl: description: 'GL is the newest slice of ledger detail, as the audit trail behind the statements. It is CAPPED, so on a busy ledger it is a sample rather than the full support for the figures above.' items: $ref: '#/components/schemas/books.GLRow' type: array org: description: "Org is the organisation whose books these are \u2014 the validated caller's own,\nstamped so a downloaded\ \ bundle still says whose it is." type: string pnl: $ref: '#/components/schemas/books.PnL' description: PnL is the income statement for the period, on an accrual basis. to: description: To closes it. Absent means up to now. type: string trialBalance: $ref: '#/components/schemas/books.TrialBalance' description: TrialBalance is the proof the ledger balances over the period. type: object books.GLRow: properties: account: description: Account is the chart-of-accounts number this leg posts to. type: string against: description: "Against names the OTHER accounts in the same voucher \u2014 the contra side of this\nleg \u2014 so\ \ a single row reads as an entry rather than as half of one." type: string credit: description: Credit is the amount credited to that account, in whole cents. format: int64 type: integer debit: description: 'Debit is the amount debited to that account, in whole cents. Exactly one of debit and credit is non-zero on a leg; a negative amount is never used to mean the other side.' format: int64 type: integer id: description: 'ID is the entry''s position in the ledger. The ledger is append-only, so ids ascend with posting order and a higher id is a later entry.' format: int64 type: integer postingAt: description: "PostingAt is the accounting date this entry belongs to \u2014 what the reports\nwindow on, which need\ \ not be when the row was written." type: string remarks: description: Remarks is the memo carried onto the entry, for a human reading the ledger. type: string sourceId: description: SourceID identifies that originating record within its kind. type: string sourceKind: description: 'SourceKind is what caused the posting: a bank line, a scanned document, a commerce sale. With sourceId it traces the entry back to the thing that produced it.' type: string type: object books.InboxItem: properties: category: description: "Category is the expense account the scanner proposed, as a chart number \u2014 a\nPROPOSAL, not a\ \ posting: nothing is booked until it is accepted." type: string confidence: description: 'Confidence is how sure the scanner is of that reading, and is the signal for whether a person needs to check it before it is booked.' type: string createdAt: description: CreatedAt is when the document was uploaded. type: string extracted: $ref: '#/components/schemas/books.Extracted' description: 'Extracted is what the scanner read off the document. Absent until it has been scanned, so its absence is "not read yet", never "nothing on it".' filename: description: 'Filename is the name the document was uploaded under, for a person to recognise it by. It is not part of the item''s identity.' type: string id: description: 'ID is the CONTENT HASH of the uploaded bytes, which is what makes the queue idempotent: re-uploading the same document returns this item rather than adding a second one. It is also the id the scan of this document carries.' type: string status: description: "Status is where the document is in the queue \u2014 unsorted until the scanner has\nread it, and thereafter\ \ whether it is waiting on a person or has been booked." type: string vendor: description: 'Vendor is the supplier the scanner identified, surfaced beside the item so a queue renders without opening each document.' type: string type: object books.Leg: properties: account: description: Account is the chart-of-accounts number this side posts to, e.g. "5300". type: string credit: description: Credit is the leg's credit in exact cents. Set this or Debit, not both. format: int64 type: integer debit: description: Debit is the leg's debit in exact cents. Set this or Credit, not both. format: int64 type: integer type: object books.LineItem: properties: amountCents: description: 'AmountCents is that line''s amount in whole cents. The scanner is instructed to return integer cents rather than a decimal, so no float rounding can enter the ledger through here.' format: int64 type: integer description: description: Description is the line as it appears on the document. type: string type: object books.MetricsResponse: properties: arr: description: "ARR is annualized recurring revenue in cents (MRR \xD7 12)." format: int64 type: integer burn: description: Burn is total expense in cents over the period. format: int64 type: integer cash: description: Cash is the bank + processor-clearing balance in cents as of To. format: int64 type: integer cogs: description: COGS is cost of goods sold in cents over the period. format: int64 type: integer deferredRevenue: description: DeferredRevenue is the customer-wallet liability in cents as of To. format: int64 type: integer figures: description: Figures is the same snapshot rendered through books' one money formatter. items: $ref: '#/components/schemas/books.Figure' type: array from: description: From is the RFC3339 start of the reporting window, exclusive; absent for all time. type: string grossMarginBps: description: GrossMarginBps is GrossProfit / Revenue in basis points (7000 = 70%). format: int64 type: integer grossProfit: description: "GrossProfit is Revenue \u2212 COGS, in cents." format: int64 type: integer monthlyBurn: description: MonthlyBurn is net cash burned per month in cents; 0 when not losing cash. format: int64 type: integer months: description: Months is the window length in whole months used to normalize MRR and burn. format: int64 type: integer mrr: description: MRR is monthly recurring revenue in cents. format: int64 type: integer netIncome: description: "NetIncome is Revenue \u2212 Burn, in cents." format: int64 type: integer period: description: Period is the human window label, e.g. "2026-07" or "all-time". type: string revenue: description: Revenue is recognized revenue in cents over the period. format: int64 type: integer runwayMonths: description: RunwayMonths is Cash / MonthlyBurn; -1 means infinite (the org is not burning). format: int64 type: integer to: description: To is the RFC3339 end of the reporting window, inclusive; absent for up to now. type: string type: object books.PnL: properties: expense: description: Expense is the cost lines that moved in the period, one per account. items: $ref: '#/components/schemas/books.PnLLine' type: array from: description: "From opens the period and is EXCLUSIVE \u2014 movement strictly after it, matching\nthe trial balance's\ \ opening boundary so the two reports agree on what belongs\nto a period. Absent means from the beginning of the\ \ ledger." type: string income: description: 'Income is the revenue lines that moved in the period, one per account. Accounts that did not move are omitted rather than listed at zero.' items: $ref: '#/components/schemas/books.PnLLine' type: array netIncome: description: NetIncome is totalIncome minus totalExpense, in cents. Negative is a loss. format: int64 type: integer to: description: To closes the period and is inclusive. Absent means up to now. type: string totalExpense: description: 'TotalExpense is cost MATCHED to that revenue, in cents, including accrued infrastructure that has not been billed yet.' format: int64 type: integer totalIncome: description: "TotalIncome is revenue RECOGNIZED in the period, in cents \u2014 accrual, not cash,\nso a prepaid\ \ top-up is not in it until the credit is consumed." format: int64 type: integer type: object books.PnLLine: properties: account: description: Account is the chart-of-accounts number this line reports on. type: string amount: description: "Amount is the account's movement over the period in whole cents, in its\nNATURAL sign: positive when\ \ the account behaved normally, for income and\nexpense alike. Income is credit-normal so its stored net is flipped\ \ once here\nfor display; the ledger underneath is never sign-flipped. A negative amount\ntherefore means the\ \ account ran backwards \u2014 a refunded sale, a reversed cost." format: int64 type: integer name: description: Name is that account's human name from the fixed chart. type: string type: description: "Type is the account's fundamental class, which on this statement is always\nincome or expense \u2014\ \ it tells a reader which half of the statement the line\ncame from without re-deriving it from the array it arrived\ \ in." type: string type: object books.Question: properties: account: description: Account is the chart number the questioned entry posted to, where one applies. type: string amount: description: "Amount is the figure that makes the question concrete, already FORMATTED for\ndisplay with its currency\ \ symbol \u2014 a string, not cents, and not for arithmetic." type: string id: description: 'ID is the source transaction the question is about, so answering it leads straight back to the entry that raised it.' type: string kind: description: 'Kind is what looked wrong: outlier (a charge far above the usual), reversal (a posting undone), roundoff (a balancing plug big enough to be worth explaining), uncosted (revenue booked with no cost matched to it), or overdrawn (a wallet spent past its balance).' type: string postedAt: description: "PostedAt anchors the question in time \u2014 when the entry it concerns posted." type: string text: description: Text is the question itself, written for a founder to answer directly. type: string type: object books.QuestionsResponse: properties: questions: description: "Questions are what the books want explained, SHARPEST FIRST \u2014 largest amounts\nahead of smaller\ \ ones, and capped, so this is the top of the list rather than\neverything. Empty means the ledger looks clean;\ \ the detector is deterministic\nover what was posted and invents nothing." items: $ref: '#/components/schemas/books.Question' type: array type: object books.Rule: properties: category: description: 'Category is the COA expense account a matching bill books to. An upsert normalizes a slug ("cloud") to its account number.' type: string pattern: description: 'Pattern is the merchant substring the rule matches on, case-insensitively. It is also the key an upsert writes by.' type: string priority: description: 'Priority breaks ties: when several patterns match, the highest wins.' format: int64 type: integer type: object books.TrialBalance: properties: balanced: description: 'Balanced is the proof this report exists to give: whether total debits equal total credits. It is computed from the rows above, never assumed, and false means the ledger itself is broken rather than that the report is wrong.' type: boolean from: description: 'From is the posting time the window opens at, as it was asked for. Absent means the report runs from the beginning of the ledger.' type: string rows: description: 'Rows are the accounts that MOVED in one of the windows. An account that never moved is omitted rather than listed at zero, so this is shorter than the chart.' items: $ref: '#/components/schemas/books.TrialBalanceRow' type: array to: description: "To is the posting time the window closes at, inclusive. Absent means \"up to\nnow\" \u2014 every posting\ \ the ledger holds." type: string totalCredit: description: TotalCredit is the sum of every row's closing credit column, in cents. format: int64 type: integer totalDebit: description: TotalDebit is the sum of every row's CLOSING debit column, in cents. format: int64 type: integer type: object books.TrialBalanceRow: properties: account: description: "Account is the chart-of-accounts NUMBER this line reports on (\"1000\", \"4000\")\n\u2014 the stable\ \ posting key, not a display label." type: string closingCredit: description: ClosingCredit is that closing balance in cents when it is a credit balance. format: int64 type: integer closingDebit: description: 'ClosingDebit is the balance at the end of the window, in cents, when it is a debit balance. This is the column the report''s totals are summed from.' format: int64 type: integer credit: description: Credit is the same window movement in cents when it was net credit. format: int64 type: integer debit: description: "Debit is the account's MOVEMENT within the window \u2014 closing minus opening, not\nthe closing balance\ \ \u2014 in cents, when that movement was net debit. Zero when the\naccount moved net credit." format: int64 type: integer name: description: Name is that account's human name from the fixed chart. type: string openingCredit: description: 'OpeningCredit is the same opening balance in cents when it fell on the credit side. Zero when the balance was a debit one.' format: int64 type: integer openingDebit: description: "OpeningDebit is the account's balance before the window began, in whole cents,\nwhen that balance\ \ was on the debit side. Zero when the balance was a credit\none \u2014 the pair is exclusive, never two halves\ \ of one number." format: int64 type: integer type: description: "Type is the account's fundamental class \u2014 asset, liability, income, expense or\nequity \u2014\ \ which is also its normal balance side. It is carried for presentation\nand does NOT decide which column an amount\ \ lands in: placement follows the\nsign of the real net, so a contra balance shows up as one." type: string type: object books.Txn: properties: amountCents: description: "AmountCents is the voucher's total, in whole cents \u2014 its total debit, which\nequals its total\ \ credit because every voucher balances. It is the size of the\nentry and carries no direction; the category says\ \ which way it went." format: int64 type: integer category: description: "Category is the chart-of-accounts NUMBER of the income or expense account this\nvoucher touched \u2014\ \ where it lands on the P&L, not a free-text label." type: string categoryName: description: 'CategoryName is that account''s human name, so a caller need not carry the chart to render the row.' type: string date: description: "Date is when the voucher POSTED \u2014 the accounting date the reports window on,\nwhich for an imported\ \ bank row is the bank's date and not the day it landed\nhere." type: string description: description: 'Description is the line a person reads: the memo carried in from the source.' type: string source: description: 'Source is where the entry came from: bank_txn for an imported statement line, scan for a receipt or bill read by the scanner, commerce_txn for a sale booked by the store.' type: string vendor: description: "Vendor is the counterparty, resolved from whatever the source knew \u2014 a bank\nrow's merchant,\ \ a scanned bill's supplier. Absent when the source named none." type: string voucherId: description: 'VoucherID identifies the underlying double-entry voucher, so a caller can open the full set of legs behind this single register line.' format: int64 type: integer type: object books.VendorRow: properties: aliases: description: 'Aliases are the other spellings a receipt may print the vendor under; a scan matching any of them resolves to this vendor.' items: type: string type: array canonical: description: Canonical is the vendor's one true name, and the key an upsert writes by. type: string defaultCategory: description: 'DefaultCategory is the COA expense account new bills from this vendor book to. An upsert normalizes a slug ("software") to its account number.' type: string type: object books.Voucher: properties: description: description: Description is the human line for the event, e.g. the vendor a bill came from. type: string legs: description: "Legs are the sides of the posting. They must balance: \u03A3debit == \u03A3credit, give or\ntake the\ \ 2\xA2 round-off allowance." items: $ref: '#/components/schemas/books.Leg' type: array postingAt: description: "PostingAt is the RFC3339 instant the event posts at \u2014 the time every statement\nwindow filters\ \ on." type: string sourceId: description: 'SourceID is the source event''s own id within that namespace. Together with SourceKind it is the key that makes a repeat posting a no-op.' type: string sourceKind: description: SourceKind is the idempotency namespace naming what booked this, e.g. "scan". type: string type: object books.inboxOut: properties: items: description: Items is every document still unsorted or in draft, newest first. items: $ref: '#/components/schemas/books.InboxItem' type: array type: object books.rulesOut: properties: rules: description: "Rules is every rule the org has set, highest priority first \u2014 the order they\nare matched in." items: $ref: '#/components/schemas/books.Rule' type: array type: object books.syncTally: properties: live: description: Live is the number of vouchers newly posted to the live ledger. format: int64 type: integer sandbox: description: Sandbox is the number newly posted to the sandbox ledger. format: int64 type: integer type: object books.transactionsOut: properties: transactions: description: Transactions is the matching register rows, newest first. items: $ref: '#/components/schemas/books.Txn' type: array type: object books.unreconciledOut: properties: questions: description: Questions is the open clarifying question per unmatched inflow. items: $ref: '#/components/schemas/books.BankQuestion' type: array transactions: description: Transactions is every bank row still unmatched against the ledger. items: $ref: '#/components/schemas/books.BankTxnRow' type: array type: object books.vendorsOut: properties: vendors: description: Vendors is every vendor the org has recorded, canonical name ascending. items: $ref: '#/components/schemas/books.VendorRow' type: array type: object bot.BotMember: properties: active: description: 'Active is whether the agent projects as a LIVE space member, derived from its registry status: empty, "active" and "ready" are live, anything else (archived/retired) is not. An inactive bot drops out of the roster while its past authorship survives.' type: boolean id: description: ID is the agent id. type: string name: description: Name is the display name. type: string personRef: description: PersonRef is the projected Person _id. type: string userId: description: UserID is the derived member account uuid (personUuid). type: string type: object bot.BotRoster: properties: bots: description: "Bots is one entry per bot, each carrying the member account uuid and the\nPerson reference the space\ \ roster addresses it by. Empty means the org has\nno bots \u2014 not that the roster could not be read, which\ \ is an error." items: $ref: '#/components/schemas/bot.BotMember' type: array type: object bot.BotRun: properties: runId: description: 'RunID is the run''s id in the bot runtime, and the node id its live VNC session is registered under.' type: string sessionUrl: description: 'SessionURL is the live session the hanzo.app /vnc panel embeds to watch or attach to this run. Derived here from the run id, never sent by the runtime.' type: string startedAt: description: StartedAt is when the run began, RFC 3339, as the runtime stamped it. type: string status: description: 'Status is the run''s state as the runtime reports it; "running" when the runtime names none of its own.' type: string surface: description: 'Surface is what the bot drives: the desktop or terminal sandbox it runs in.' type: string task: description: Task is the instruction the bot is executing. type: string type: object bot.BotRuns: properties: bots: description: Bots is the org's live runs. Always an array, never null. items: $ref: '#/components/schemas/bot.BotRun' type: array type: object bot.BotStopped: properties: runId: description: RunID is the run that was stopped. type: string status: description: 'Status is the run''s terminal state: "stopped".' type: string type: object bot.BotSync: properties: projected: description: Projected is how many roster entries the reconcile touched. format: int64 type: integer synced: description: Synced is true when the reconcile ran. type: boolean type: object browserTagOut: properties: id: type: string platform: type: string type: type: string type: object buildRef: properties: id: type: string image: type: string job: type: string ref: type: string repo: type: string status: type: string type: object campaign.ChannelMetric: properties: externalId: description: 'ExternalID is the provider-side id of the execution the spend belongs to. Absent until the channel has launched.' type: string kind: description: "Kind is which channel this row is: paid, organic or email. It is also the\nrow's identity \u2014 a\ \ campaign carries at most one channel per kind." type: string platform: description: 'Platform is the provider the spend was read from: meta, google, x, instagram, or the email provider.' type: string spendCents: description: "SpendCents is what the provider itself reports this channel spent, in CENTS.\n0 when the channel never\ \ launched, when no executor is wired for it, or when\nthe read failed \u2014 SpendError tells the last case apart\ \ from a genuine zero." format: int64 type: integer spendError: description: 'SpendError is why this channel''s spend could not be read (connector not connected, provider error), as one secret-free line. Present only on failure; the campaign total then simply omits this channel rather than failing.' type: string status: description: "Status is the channel's launch state on the campaign \u2014 pending, live, paused,\nfailed or unavailable.\ \ Only a live channel is asked for its spend at all." type: string type: object campaign.ChannelSpec: properties: account: description: 'Account is the provider account this channel runs under: an ad-account, a page or a mailing-list id. An executor may replace it at launch with the account it actually used.' type: string detail: description: "Detail is the last outcome in one secret-free line \u2014 the failure reason, or\nwhat the executor\ \ reported. Absent when there is nothing to explain." type: string externalId: description: 'ExternalID is the provider-side id of the running execution, recorded by the orchestrator at launch and handed back verbatim to read spend or to pause. Server-owned and absent until this channel has launched; anything a caller sends for it is dropped.' type: string kind: description: 'Kind is the channel and the identity a campaign holds at most one of: paid, organic or email. It picks the executor the launch fans out to.' type: string platform: description: "Platform is the provider within the kind \u2014 meta, google, x, instagram, or the\nemail provider." type: string status: description: "Status is this channel's own launch outcome, not the campaign's: pending (added,\nnever launched),\ \ live, paused, failed (Detail says why) or unavailable (no\nexecutor wired on this deployment). Server-owned\ \ \u2014 a caller can never assert it." type: string type: object campaign.campaignPage: properties: data: description: Data are the campaigns on this page. items: $ref: '#/components/schemas/campaign.campaignRecord' type: array type: object campaign.campaignRecord: properties: audience: description: "Audience is an opaque reference to the segment this campaign targets. It is\nstored and echoed but\ \ not yet handed to the executors \u2014 a channel targets\nthrough the provider account it runs under \u2014\ \ so it is documentation for now.\nAbsent when never set." type: string budget: description: 'Budget is the campaign''s total budget in CENTS, handed to each executor as the budget for its channel. 0 means none was set.' format: int64 type: integer channels: description: 'Channels are the fan-out targets, at most one per kind and at most 12, each carrying its own post-launch state. Empty means nothing to launch, which is what makes a launch of this campaign a 400.' items: $ref: '#/components/schemas/campaign.ChannelSpec' type: array content: description: 'Content is the ordered creative set, at most 32, empty entries dropped. Content[0] is the creative that runs; the rest are A/B variants a wired experiment can assign per launch.' items: type: string type: array createdAt: description: CreatedAt is when the campaign was created, in unix seconds. Server-set. format: int64 type: integer id: description: "ID is the campaign's server-minted handle \u2014 \"cmp_\" and 128 random bits \u2014 and\nthe id every\ \ other campaign call is addressed by. Never read off the wire: a\ncreate that sends one has it ignored." type: string name: description: 'Name is the campaign''s display name. Required on write, trimmed, and capped at 2048 characters.' type: string scheduleAt: description: 'ScheduleAt is when the campaign should run, in unix seconds. 0 (absent) means launch immediately. It is passed to each executor; nothing in this service wakes up to launch it for you.' format: int64 type: integer status: description: "Status is the lifecycle state, server-owned and never accepted from a caller.\nFour values actually\ \ occur: draft (inert and fully mutable \u2014 nothing is sent\nand no budget is committed), live, paused and\ \ failed. After a fan-out live\nmeans AT LEAST ONE channel launched \u2014 read the channel rows for the rest\ \ \u2014\nand failed means none did." type: string updatedAt: description: "UpdatedAt is the last write in unix seconds \u2014 an edit, a launch or a pause.\nServer-set on every\ \ save." format: int64 type: integer type: object campaign.campaignResults: properties: abTest: description: "ABTest is the creative A/B analysis from the experiments primitive\n(experiment.Analyze, pull-model),\ \ present only when the campaign runs\nmore than one creative and an experiment is wired. Opaque JSON \u2014 campaign\n\ stays decoupled from the experiments analysis type." available: description: "Available is false when the analytics warehouse is not connected or the query\nfailed: the funnel\ \ below is then zero because nothing could be read, not\nbecause nothing happened. Spend and Channels are still\ \ real \u2014 they come from\nthe connectors, not the warehouse." type: boolean cac: description: "CAC is customer acquisition cost: spend DOLLARS per conversion, rounded to\ncents. 0 when nothing\ \ converted \u2014 that is \"not yet computable\", not \"free\"." format: double type: number campaignId: description: CampaignID is the campaign these results are for, echoed from the request. type: string channels: description: 'Channels is the per-channel spend breakdown that SpendCents sums, one row per channel on the campaign including the ones that never launched.' items: $ref: '#/components/schemas/campaign.ChannelMetric' type: array clicks: description: Clicks is the campaign's click events over the window. format: int64 type: integer conversions: description: "Conversions is the terminal funnel events attributed to the campaign \u2014 orders\ncompleted, signups\ \ completed, explicit conversion events." format: int64 type: integer ctr: description: 'CTR is clicks per impression, a fraction rounded to 4 places (0.0123 = 1.23%), not a percentage. 0 when there were no impressions to divide by.' format: double type: number cvr: description: 'CVR is conversions per click, a fraction rounded to 4 places. 0 when there were no clicks.' format: double type: number end: description: "End is the window's end, RFC3339 UTC \u2014 the read's own clock unless an explicit\npair was given.\ \ The window is a LOOKBACK, not the campaign's own lifetime." type: string impressions: description: 'Impressions is how many times the campaign''s creatives were shown, counted from its utm_campaign-tagged impression events.' format: int64 type: integer name: description: 'Name is the campaign''s display name at read time, so a result can be labelled without a second fetch.' type: string range: description: 'Range is the window actually used: 24h, 7d, 30d, 90d, or "custom" when an explicit start/end pair was honored. An unparseable or absent range reads 30d, so this is the value to trust, not the one that was sent.' type: string revenue: description: "Revenue is the summed revenue attribute of the campaign's events, in whole\nCURRENCY UNITS (dollars)\ \ \u2014 the one money value here that is not in cents." format: double type: number roas: description: 'ROAS is return on ad spend: revenue per spend DOLLAR, rounded to 2 places (2.5 = $2.50 back per $1). 0 when nothing was spent.' format: double type: number source: description: 'Source names the analytics table the funnel was read from, so an operator can see exactly what was counted. Set even when Available is false.' type: string spendCents: description: 'SpendCents is the campaign''s total spend in CENTS: the sum of what each live channel''s provider reports. A channel whose spend could not be read contributes 0 and says so on its own row.' format: int64 type: integer start: description: Start is the window's inclusive start, RFC3339 UTC. type: string status: description: "Status is the campaign's lifecycle state at read time \u2014 draft, live, paused,\ncompleted or failed.\ \ A draft has never run, so its funnel is legitimately zero." type: string visitors: description: "Visitors is how many distinct people the campaign reached, counted by event\nidentity across ALL its\ \ events in the window \u2014 not a subset of Impressions, so\nit can exceed them for a campaign whose provider\ \ reports clicks but not views." format: int64 type: integer type: object campaign.campaignSummary: properties: budget: description: Budget is the sum of every campaign's budget, in CENTS. format: int64 type: integer campaigns: description: Campaigns is how many campaigns the org has, in any state. format: int64 type: integer channels: description: 'Channels are the channel kinds this deployment has an executor wired for. A kind absent here is a kind a launch will honestly record as unavailable.' items: type: string type: array live: description: Live is how many of them are currently live. format: int64 type: integer type: object campaign.campaignUpdate: properties: audience: type: string budget: format: int64 type: integer channels: items: $ref: '#/components/schemas/campaign.ChannelSpec' type: array content: items: type: string type: array id: description: ID is the campaign to update, from the path. type: string name: type: string scheduleAt: format: int64 type: integer type: object campaign.campaignWrite: properties: audience: description: Audience is the segment or audience selector this campaign targets. type: string budget: description: Budget is the campaign's total budget in CENTS. Negative reads as 0. format: int64 type: integer channels: description: 'Channels are the fan-out targets, at most one per kind (paid, organic, email) and at most 12. A channel''s status and provider id are server-owned: whatever the caller sends for them is replaced with "pending".' items: $ref: '#/components/schemas/campaign.ChannelSpec' type: array content: description: 'Content is the ordered creative set. Content[0] is the active creative and the rest are A/B variants; at most 32, empty entries dropped.' items: type: string type: array name: description: 'Name is the campaign''s display name. Required; trimmed and capped at 2048 characters.' type: string scheduleAt: description: 'ScheduleAt is when the campaign should run, in unix seconds. Negative reads as 0 (immediately).' format: int64 type: integer type: object campaign.channelAdd: properties: account: description: 'Account is the provider account this channel runs under: an ad-account, a page, or a mailing-list id.' type: string id: description: ID is the campaign to add the channel to, from the path. type: string kind: description: 'Kind is the channel kind and the identity a campaign holds at most one of: paid, organic or email.' type: string platform: description: "Platform is the provider within the kind \u2014 meta, google, x, instagram, or\nthe email provider." type: string type: object captable.captableClassHolding: properties: authorized: description: Authorized is how many shares of the class are authorized. format: int64 type: integer classType: description: ClassType is COMMON or PREFERRED. type: string issued: description: Issued is how many shares of the class have been issued. format: int64 type: integer name: description: Name is the class name. type: string shareClassId: description: ShareClassID addresses the class this position is for. type: string type: object captable.captableCompany: properties: createdAt: description: CreatedAt is when the company row was seeded, in unix milliseconds. format: int64 type: integer id: description: ID is the company id, which is the tenant's own org id. type: string incorporationCountry: description: IncorporationCountry is the ISO country the entity is incorporated in. type: string incorporationState: description: IncorporationState is the state or province of incorporation. type: string incorporationType: description: IncorporationType is the entity kind, e.g. LLC or C_CORP. type: string name: description: Name is the company's legal name. type: string publicId: description: PublicID is the company's shareable public identifier. type: string updatedAt: description: UpdatedAt is when the company row last changed, in unix milliseconds. format: int64 type: integer type: object captable.captableCompanyUpdate: properties: incorporationCountry: description: 'IncorporationCountry is the ISO country the entity is incorporated in. Optional; omitted, null or empty clears it. Any JSON scalar is accepted and stored as its text.' incorporationState: description: 'IncorporationState is the state or province of incorporation. Optional; omitted, null or empty clears it. Any JSON scalar is accepted and stored as its text.' incorporationType: description: 'IncorporationType is the entity kind, e.g. LLC or C_CORP. Optional; omitted, null or empty clears it. Any JSON scalar is accepted and stored as its text.' name: description: "Name is the company's legal name. Required, and it must be a non-empty\nstring \u2014 anything else\ \ is refused with the cap table's own validation\nerror." type: object captable.captableConvertibleIn: properties: additionalTerms: description: AdditionalTerms is free text for anything the fields above do not carry. Optional. boardApprovalDate: description: BoardApprovalDate is when the board approved it, YYYY-MM-DD. capital: description: Capital is the principal lent, in the company's currency. conversionCap: description: ConversionCap is the valuation the note converts at, at most. Optional. discountRate: description: DiscountRate is the discount to the next round's price, as a percentage. Optional. interestRate: description: InterestRate is the annual interest the principal accrues, as a percentage. Optional. issueDate: description: IssueDate is when the note was issued, YYYY-MM-DD. publicId: description: PublicID is the note's identifier. It must be UNIQUE within the company; reusing one is refused 409. stakeholderId: description: StakeholderID is the lender. It must name a stakeholder of THIS company. status: description: Status is DRAFT, ACTIVE, PENDING, EXPIRED or CANCELLED. type: description: Type is CCD, OCD or NOTE. type: object captable.captableConvertibles: properties: notes: $ref: '#/components/schemas/captable.captableInstrumentTotal' description: Notes is the convertible notes total. safes: $ref: '#/components/schemas/captable.captableInstrumentTotal' description: Safes is the SAFEs total. type: object captable.captableCreated: properties: id: description: ID is the created row's id. type: string message: description: Message is the human sentence the cap table wrote, e.g. "Share issued". type: string success: description: Success is true when the row was written. type: boolean type: object captable.captableDeleted: properties: success: description: Success is true when the row was removed. type: boolean type: object captable.captableEquityPlan: properties: boardApprovalDate: description: BoardApprovalDate is the ISO date the board approved the plan. type: string comments: description: Comments is free-form notes on the plan. type: string createdAt: description: CreatedAt is when the plan was recorded, in unix milliseconds. format: int64 type: integer defaultCancellatonBehavior: description: 'DefaultCancellatonBehavior is what happens to cancelled grants, RETIRE or RETURN_TO_POOL. The key is spelled as the cap-table wire spells it.' type: string id: description: ID is the equity plan id. type: string initialSharesReserved: description: InitialSharesReserved is how many shares the plan reserves. format: int64 type: integer name: description: Name is the plan name, e.g. "2026 Stock Option Plan". type: string planEffectiveDate: description: PlanEffectiveDate is the ISO date the plan takes effect. type: string shareClassId: description: ShareClassID is the class the reserved shares come from. type: string type: object captable.captableEquityPlanIn: properties: boardApprovalDate: description: BoardApprovalDate is when the board adopted the plan, YYYY-MM-DD. comments: description: Comments is a free-text note kept with the plan. Optional. defaultCancellatonBehavior: description: "DefaultCancellatonBehavior is what happens to cancelled grants: RETIRE, RETURN_TO_POOL, HOLD_AS_CAPITAL_STOCK\ \ or DEFINED_PER_PLAN_SECURITY.\n\nThe key is spelled `defaultCancellatonBehavior` \u2014 one `l` \u2014 because\ \ that is\nthe wire, and correcting it here would silently drop the field." initialSharesReserved: description: InitialSharesReserved is how many shares the plan may grant. A whole number. name: description: Name is the plan's name, e.g. "2026 Stock Option Plan". planEffectiveDate: description: PlanEffectiveDate is when the plan takes effect, YYYY-MM-DD. Optional. shareClassId: description: ShareClassID is the class the plan grants from. It must name a class of THIS company. type: object captable.captableEquityPlans: properties: data: description: Data is every equity plan on the caller org's cap table, newest first. items: $ref: '#/components/schemas/captable.captableEquityPlan' type: array type: object captable.captableHolding: properties: fullyDiluted: description: FullyDiluted is shares plus options. format: int64 type: integer name: description: Name is the stakeholder's name. type: string options: description: Options is the shares under this stakeholder's non-terminal option grants. format: int64 type: integer ownershipPct: description: 'OwnershipPct is fullyDiluted as a percentage of the company''s fullyDilutedShares, rounded to two decimals; 0 when nothing is issued.' format: double type: number shares: description: Shares is the shares this stakeholder holds by certificate. format: int64 type: integer stakeholderId: description: StakeholderID addresses the stakeholder these totals are for. type: string type: object captable.captableInstrumentTotal: properties: capital: description: Capital is the total capital across those instruments. format: double type: number count: description: Count is how many instruments there are. format: int64 type: integer type: object captable.captableInvested: properties: id: description: ID is the investment record's id. type: string message: description: Message is the human sentence the cap table wrote. type: string newShareId: description: 'NewShareID names the certificate the investment issued, when the round carries a price per share. Null when the round prices later, which is a recorded investment and not a failure.' type: string success: description: Success is true when the investment was recorded. type: boolean type: object captable.captableInvestment: properties: amount: description: Amount is the cash invested. format: double type: number date: description: Date is the ISO date of the investment. type: string id: description: ID is the investment id. type: string roundId: description: RoundID is the round the cheque went into. type: string shareClassId: description: ShareClassID is the class shares were issued in, for a priced round. type: string shares: description: 'Shares is how many shares the investment bought; 0 when the round issues no equity at the time of investment.' format: int64 type: integer stakeholderId: description: StakeholderID is the investor. type: string stakeholderName: description: StakeholderName is that investor's name. type: string type: object captable.captableInvestmentIn: properties: amount: description: 'Amount is the money invested, in the round''s own currency. Required and must be positive.' comments: description: Comments is a free-text note kept with the investment. Optional. date: description: 'Date is when the investment was made, YYYY-MM-DD. Optional: omitted, it is recorded as today.' id: description: "ID is the round to invest in. The URL is the addressing authority \u2014 a path\nsegment binds after\ \ the body and after the query \u2014 so the address decides\nwhich round is written whatever a body claims." type: string stakeholderId: description: 'StakeholderID is the investor. Required, and it must reference a stakeholder of the same company.' type: object captable.captableInvestments: properties: data: description: Data is every investment across every round, newest first. items: $ref: '#/components/schemas/captable.captableInvestment' type: array type: object captable.captableNote: properties: capital: description: Capital is the principal the investor lent. format: double type: number conversionCap: description: ConversionCap is the valuation cap on conversion, if any. format: double type: number discountRate: description: DiscountRate is the discount to the next round's price, if any. format: double type: number id: description: ID is the note id. type: string interestRate: description: InterestRate is the annual interest rate, if any. format: double type: number issueDate: description: IssueDate is the ISO date the note was signed. type: string publicId: description: PublicID is the note's shareable identifier, unique within the company. type: string stakeholderId: description: StakeholderID is the investor. type: string stakeholderName: description: StakeholderName is that investor's name. type: string status: description: Status is the note's state, e.g. DRAFT or ACTIVE. type: string type: description: Type is the instrument kind, e.g. NOTE. type: string type: object captable.captableNotes: properties: data: description: Data is every convertible note, newest first. items: $ref: '#/components/schemas/captable.captableNote' type: array type: object captable.captableOption: properties: cliffYears: description: CliffYears is how many years before any of the grant vests. format: int64 type: integer equityPlanId: description: EquityPlanID is the plan the grant draws from. type: string equityPlanName: description: EquityPlanName is that plan's name. type: string exercisePrice: description: ExercisePrice is the strike price per share. format: double type: number expirationDate: description: ExpirationDate is the ISO date the grant expires. type: string grantId: description: GrantID is the grant number, unique within the company. type: string id: description: ID is the option id. type: string issueDate: description: IssueDate is the ISO date the grant was issued. type: string quantity: description: Quantity is how many shares the grant covers. format: int64 type: integer stakeholderId: description: StakeholderID is the grantee. type: string stakeholderName: description: StakeholderName is that grantee's name. type: string status: description: 'Status is the grant''s state, e.g. DRAFT, ACTIVE, EXERCISED, EXPIRED or CANCELLED. Only non-terminal grants dilute the cap table.' type: string type: description: Type is the grant kind, ISO or NSO. type: string vestingYears: description: VestingYears is the total vesting period in years. format: int64 type: integer type: object captable.captableOptionIn: properties: boardApprovalDate: description: BoardApprovalDate is when the board approved the grant, YYYY-MM-DD. cliffYears: description: CliffYears is how long before ANY of the grant vests. A whole number; 0 for no cliff. equityPlanId: description: EquityPlanID is the plan the options come from. It must name a plan of THIS company. exercisePrice: description: ExercisePrice is the strike price per share, in the company's currency. expirationDate: description: ExpirationDate is when unexercised options lapse, YYYY-MM-DD. grantId: description: GrantID is the grant's identifier. It must be UNIQUE within the company; reusing one is refused 409. issueDate: description: IssueDate is when the grant was made, YYYY-MM-DD. notes: description: Notes is a free-text note kept with the grant. Optional. quantity: description: Quantity is how many options to grant. A whole number above 0. rule144Date: description: Rule144Date is when the Rule 144 holding period starts, YYYY-MM-DD. stakeholderId: description: StakeholderID is who receives the grant. It must name a stakeholder of THIS company. status: description: Status is DRAFT, ACTIVE, EXERCISED, EXPIRED or CANCELLED. type: description: Type is ISO, NSO or RSU. vestingStartDate: description: VestingStartDate is when vesting begins, YYYY-MM-DD. vestingYears: description: VestingYears is the total vesting period. A whole number. type: object captable.captableOptions: properties: data: description: Data is every option grant, newest first. items: $ref: '#/components/schemas/captable.captableOption' type: array type: object captable.captableRound: properties: closeDate: description: CloseDate is the ISO date the round closed, once it has. type: string createdAt: description: CreatedAt is when the round was recorded, in unix milliseconds. format: int64 type: integer id: description: ID is the round id. type: string name: description: Name is the round name, e.g. "Series A". type: string preMoneyValuation: description: PreMoneyValuation is the pre-money valuation, for a priced round. format: double type: number pricePerShare: description: PricePerShare is the price per share, for a priced round. format: double type: number raisedAmount: description: RaisedAmount is how much has been invested so far. format: double type: number roundType: description: RoundType is PRICED, SAFE or CONVERTIBLE_NOTE. type: string shareClassId: description: ShareClassID is the class a priced round issues into. type: string status: description: Status is OPEN or CLOSED. type: string targetAmount: description: TargetAmount is how much the round set out to raise. format: double type: number type: object captable.captableRoundCloseRequest: properties: closeDate: description: 'CloseDate is the date to record the round as closed on. Optional: omitted, null or empty records TODAY. Any JSON scalar is accepted and stored as its text, and the text is stored unparsed, so a caller that wants an ISO date sends one.' id: description: 'ID is the round to close. It is the path segment: the URL is the addressing authority, and the org it is resolved in comes from the caller''s principal, so an id from another tenant is simply not found.' type: string type: object captable.captableRoundDetail: properties: investments: description: Investments is every investment into this round, oldest first. items: $ref: '#/components/schemas/captable.captableRoundInvestment' type: array round: $ref: '#/components/schemas/captable.captableRound' description: "Round is the round's own terms \u2014 name, type, valuation, target and status \u2014\nas against\ \ the investments beside it." type: object captable.captableRoundIn: properties: name: description: Name is the round's name, e.g. "Series A". preMoneyValuation: description: PreMoneyValuation is the company's valuation before the new money. Optional. pricePerShare: description: PricePerShare is the price investors pay per share. An investment into a round with a price ISSUES the shares it buys; one into a round priced later records the money alone. roundType: description: RoundType is PRICED, SAFE or CONVERTIBLE. shareClassId: description: ShareClassID is the class investors receive. It must name a class of THIS company. targetAmount: description: TargetAmount is how much the round aims to raise, in the company's currency. type: object captable.captableRoundInvestment: properties: amount: description: Amount is the cash invested. format: double type: number comments: description: Comments is the note recorded with the cheque, if any. type: string date: description: Date is the ISO date of the investment. type: string id: description: ID is the investment id. type: string shares: description: 'Shares is how many shares the investment bought; 0 when the round issues no equity at the time of investment.' format: int64 type: integer stakeholderId: description: StakeholderID is the investor. type: string stakeholderName: description: StakeholderName is that investor's name. type: string type: object captable.captableRoundTotals: properties: count: description: Count is how many rounds the company has recorded. format: int64 type: integer totalRaised: description: TotalRaised is the sum of every round's raised amount. format: double type: number type: object captable.captableRounds: properties: data: description: Data is every round, newest first. items: $ref: '#/components/schemas/captable.captableRound' type: array type: object captable.captableSafe: properties: capital: description: Capital is the cash the investor put in. format: double type: number discountRate: description: DiscountRate is the discount to the next round's price, if any. format: double type: number id: description: ID is the SAFE id. type: string issueDate: description: IssueDate is the ISO date the SAFE was signed. type: string mfn: description: MFN is true when the SAFE carries a most-favoured-nation clause. type: boolean proRata: description: ProRata is true when the SAFE carries pro-rata rights. type: boolean publicId: description: PublicID is the SAFE's shareable identifier, unique within the company. type: string stakeholderId: description: StakeholderID is the investor. type: string stakeholderName: description: StakeholderName is that investor's name. type: string status: description: Status is the SAFE's state, e.g. DRAFT or ACTIVE. type: string type: description: Type is POST_MONEY or PRE_MONEY. type: string valuationCap: description: ValuationCap is the valuation cap, if any. format: double type: number type: object captable.captableSafeIn: properties: additionalTerms: description: AdditionalTerms is free text for anything the fields above do not carry. Optional. boardApprovalDate: description: BoardApprovalDate is when the board approved it, YYYY-MM-DD. capital: description: Capital is the money invested, in the company's currency. discountRate: description: DiscountRate is the discount to the next round's price, as a percentage. Optional. issueDate: description: IssueDate is when the SAFE was signed, YYYY-MM-DD. publicId: description: PublicID is the SAFE's identifier. It must be UNIQUE within the company; reusing one is refused 409. stakeholderId: description: StakeholderID is the investor. It must name a stakeholder of THIS company. status: description: Status is DRAFT, ACTIVE, PENDING, EXPIRED or CANCELLED. type: description: "Type is PRE_MONEY or POST_MONEY \u2014 which side of the new money the cap is measured on." valuationCap: description: ValuationCap is the valuation the SAFE converts at, at most. Optional; omit for an uncapped SAFE. type: object captable.captableSafes: properties: data: description: Data is every SAFE, newest first. items: $ref: '#/components/schemas/captable.captableSafe' type: array type: object captable.captableShare: properties: capitalContribution: description: CapitalContribution is the cash paid for the certificate, if recorded. format: double type: number certificateId: description: CertificateID is the certificate number, unique within the company. type: string companyLegends: description: CompanyLegends are the restrictive legends printed on the certificate. items: type: string type: array id: description: ID is the share id. type: string issueDate: description: IssueDate is the ISO date the certificate was issued. type: string pricePerShare: description: PricePerShare is the price paid per share, if recorded. format: double type: number quantity: description: Quantity is how many shares the certificate covers. format: int64 type: integer shareClassId: description: ShareClassID is the class the shares belong to. type: string shareClassName: description: ShareClassName is that class's name. type: string shareClassType: description: ShareClassType is that class's type, COMMON or PREFERRED. type: string stakeholderId: description: StakeholderID is the holder of the certificate. type: string stakeholderName: description: StakeholderName is that holder's name. type: string status: description: Status is ACTIVE or DRAFT. type: string type: object captable.captableShareClass: properties: classType: description: ClassType is COMMON or PREFERRED. type: string companyName: description: CompanyName is the name of the company whose cap table this is. type: string conversionRights: description: 'ConversionRights describes what the class converts into, e.g. CONVERTS_TO_FUTURE_ROUND.' type: string id: description: ID is the share class id. type: string idx: description: Idx is the class's 1-based position within the company, in creation order. format: int64 type: integer initialSharesAuthorized: description: InitialSharesAuthorized is how many shares of this class are authorized. format: int64 type: integer liquidationPreferenceMultiple: description: LiquidationPreferenceMultiple is the preference multiple on liquidation. format: double type: number name: description: Name is the class name, e.g. "Common" or "Series A Preferred". type: string parValue: description: ParValue is the par value per share. format: double type: number participationCapMultiple: description: ParticipationCapMultiple caps participation on liquidation; 0 is uncapped. format: double type: number prefix: description: Prefix is the certificate prefix, CS for common and PS for preferred. type: string pricePerShare: description: PricePerShare is the issue price per share. format: double type: number seniority: description: Seniority orders classes in a liquidation waterfall; higher is more senior. format: int64 type: integer votesPerShare: description: VotesPerShare is how many votes one share of this class carries. format: int64 type: integer type: object captable.captableShareClassAmend: properties: boardApprovalDate: description: BoardApprovalDate is when the board approved the class, YYYY-MM-DD. classType: description: ClassType is COMMON or PREFERRED. conversionRights: description: ConversionRights is CONVERTS_TO_FUTURE_ROUND or CONVERTS_TO_SHARE_CLASS_ID. The second requires convertsToShareClassId. convertsToShareClassId: description: ConvertsToShareClassID names the class this one converts into. Required when conversionRights is CONVERTS_TO_SHARE_CLASS_ID, ignored otherwise. id: description: "ID addresses the resource. The URL is the addressing authority \u2014 a path\nsegment binds after\ \ the body and after the query \u2014 so the address decides\nwhich row is written whatever a body claims." type: string initialSharesAuthorized: description: InitialSharesAuthorized is how many shares this class may issue. A whole number. liquidationPreferenceMultiple: description: "LiquidationPreferenceMultiple is the multiple of invested capital paid out before junior classes \u2014\ \ 1 for a 1x preference." name: description: Name is the share class's name, e.g. "Series A Preferred". parValue: description: ParValue is the nominal per-share value on the certificate, in the company's currency. participationCapMultiple: description: ParticipationCapMultiple caps participation after the preference is paid, as a multiple of invested capital. 0 for no cap. pricePerShare: description: PricePerShare is the issue price for this class, in the company's currency. seniority: description: 'Seniority orders liquidation preference: LOWER is more senior, so 1 is paid before 2.' stockholderApprovalDate: description: StockholderApprovalDate is when the stockholders approved it, YYYY-MM-DD. votesPerShare: description: VotesPerShare is how many votes one share of this class carries. A whole number; 0 for non-voting. type: object captable.captableShareClassIn: properties: boardApprovalDate: description: BoardApprovalDate is when the board approved the class, YYYY-MM-DD. classType: description: ClassType is COMMON or PREFERRED. conversionRights: description: ConversionRights is CONVERTS_TO_FUTURE_ROUND or CONVERTS_TO_SHARE_CLASS_ID. The second requires convertsToShareClassId. convertsToShareClassId: description: ConvertsToShareClassID names the class this one converts into. Required when conversionRights is CONVERTS_TO_SHARE_CLASS_ID, ignored otherwise. initialSharesAuthorized: description: InitialSharesAuthorized is how many shares this class may issue. A whole number. liquidationPreferenceMultiple: description: "LiquidationPreferenceMultiple is the multiple of invested capital paid out before junior classes \u2014\ \ 1 for a 1x preference." name: description: Name is the share class's name, e.g. "Series A Preferred". parValue: description: ParValue is the nominal per-share value on the certificate, in the company's currency. participationCapMultiple: description: ParticipationCapMultiple caps participation after the preference is paid, as a multiple of invested capital. 0 for no cap. pricePerShare: description: PricePerShare is the issue price for this class, in the company's currency. seniority: description: 'Seniority orders liquidation preference: LOWER is more senior, so 1 is paid before 2.' stockholderApprovalDate: description: StockholderApprovalDate is when the stockholders approved it, YYYY-MM-DD. votesPerShare: description: VotesPerShare is how many votes one share of this class carries. A whole number; 0 for non-voting. type: object captable.captableShareIn: properties: boardApprovalDate: description: BoardApprovalDate is when the board approved the issuance, YYYY-MM-DD. capitalContribution: description: CapitalContribution is cash paid for the shares. Optional. certificateId: description: CertificateID is the certificate number. It must be UNIQUE within the company; reusing one is refused 409. cliffYears: description: CliffYears is how long before ANY of the grant vests. A whole number; 0 for no cliff. companyLegends: description: "CompanyLegends are the restrictive legends printed on the certificate.\n\nA LIST, and it must be sent\ \ as one: the bundle substitutes an EMPTY list for\nanything that is not an array, so a single string would be\ \ accepted and\nsilently discarded \u2014 the certificate issued with no legends and nothing\nreporting it." items: {} type: array debtCancelled: description: DebtCancelled is debt forgiven as consideration. Optional. ipContribution: description: IPContribution is intellectual property assigned as consideration, valued in the company's currency. Optional. issueDate: description: IssueDate is when the shares were issued, YYYY-MM-DD. otherContributions: description: OtherContributions is any other consideration, valued in the company's currency. Optional. pricePerShare: description: PricePerShare is what was paid per share. Optional. quantity: description: Quantity is how many shares to issue. A whole number above 0. rule144Date: description: Rule144Date is when the Rule 144 holding period starts, YYYY-MM-DD. Optional. shareClassId: description: ShareClassID is the class being issued. It must name a class of THIS company. stakeholderId: description: StakeholderID is who receives the shares. It must name a stakeholder of THIS company. status: description: Status is ACTIVE, DRAFT, SIGNED or PENDING. vestingStartDate: description: VestingStartDate is when vesting begins, YYYY-MM-DD. Optional; defaults to the issue date. vestingYears: description: VestingYears is the total vesting period. A whole number; 0 for fully vested at issue. type: object captable.captableShareTransfer: properties: certificateId: description: "CertificateID names the NEW certificate a partial transfer issues, and is\nrequired for one \u2014\ \ a partial transfer without it is refused 400. It is unused\nby a full transfer, which reassigns the existing\ \ certificate.\n\nIt must be unique within the company; reusing one is refused 409. Declaring\nit is not optional\ \ in the way the tag suggests: omitting this field from the\nGo type would leave `quantity` accepted and every\ \ PARTIAL transfer answering\n\"certificateId is required\" with no way for a caller to supply it \u2014 the\n\ silent-drop failure this whole conversion was blocked on, one field wide." quantity: description: 'Quantity is how many shares to move. OPTIONAL, and its absence is not zero: omitted or null transfers the WHOLE certificate. A value outside 1..held is refused, so a partial transfer can never over-issue. A full transfer REASSIGNS the certificate and mints no new one; a partial transfer splits it and answers with the new share''s id. That is the fact a caller needs from the answer, not from this comment.' shareId: description: "ShareID is the certificate being transferred. Required, and it must name a\nshare in the caller org's\ \ own company \u2014 a share id from another tenant\nresolves to not-found rather than to another company's certificate." toStakeholderId: description: 'ToStakeholderID is who receives them. Required, and it must reference a stakeholder of the SAME company; one that does not is refused 400 rather than creating a dangling holder.' type: object captable.captableShares: properties: data: description: Data is every issued share certificate, newest first. items: $ref: '#/components/schemas/captable.captableShare' type: array type: object captable.captableStakeholder: properties: city: description: City is the stakeholder's city, if recorded. type: string companyName: description: CompanyName is the name of the company whose cap table this is. type: string country: description: Country is the stakeholder's two-letter country code. type: string createdAt: description: CreatedAt is when the stakeholder was added, in unix milliseconds. format: int64 type: integer currentRelationship: description: 'CurrentRelationship is how the stakeholder relates to the company, e.g. FOUNDER, INVESTOR or EMPLOYEE.' type: string email: description: Email is the stakeholder's email, unique within the company. type: string id: description: ID is the stakeholder id. type: string institutionName: description: InstitutionName names the institution, when the stakeholder is one. type: string name: description: Name is the stakeholder's full name. type: string stakeholderType: description: StakeholderType is INDIVIDUAL or INSTITUTION. type: string state: description: State is the stakeholder's state or province, if recorded. type: string streetAddress: description: StreetAddress is the stakeholder's street address, if recorded. type: string taxId: description: TaxID is the stakeholder's tax identifier, if recorded. type: string zipcode: description: Zipcode is the stakeholder's postal code, if recorded. type: string type: object captable.captableStakeholderPatch: properties: city: description: City is the stakeholder's city. currentRelationship: description: "CurrentRelationship is how the stakeholder relates to the company, e.g.\nFOUNDER, INVESTOR or EMPLOYEE.\ \ This route stores it as sent \u2014 unlike\nadding a stakeholder, it is not checked against the vocabulary." email: description: "Email is the stakeholder's email. This route stores it as sent \u2014 unlike\nadding a stakeholder,\ \ it is not checked for shape or uniqueness." id: description: 'ID is the stakeholder to update. It is the path segment: the URL is the addressing authority, and the org it is resolved in comes from the caller''s principal, so an id from another tenant is simply not found.' type: string institutionName: description: InstitutionName names the institution, when the stakeholder is one. name: description: Name is the stakeholder's full name. stakeholderType: description: "StakeholderType is INDIVIDUAL or INSTITUTION. This route stores it as\nsent \u2014 unlike adding a\ \ stakeholder, it is not checked against the\nvocabulary." state: description: State is the stakeholder's state or province. streetAddress: description: StreetAddress is the stakeholder's street address. taxId: description: TaxID is the stakeholder's tax identifier. zipcode: description: Zipcode is the stakeholder's postal code. type: object captable.captableSummary: properties: byShareClass: description: 'ByShareClass is each share class''s authorized-versus-issued position, in class creation order.' items: $ref: '#/components/schemas/captable.captableClassHolding' type: array byStakeholder: description: ByStakeholder is each stakeholder's position, largest holding first. items: $ref: '#/components/schemas/captable.captableHolding' type: array company: $ref: '#/components/schemas/captable.captableSummaryCompany' description: Company names the company the cap table is computed for. convertibles: $ref: '#/components/schemas/captable.captableConvertibles' description: Convertibles is the capital on SAFEs and notes that have not converted. rounds: $ref: '#/components/schemas/captable.captableRoundTotals' description: Rounds is the fundraising rollup. totals: $ref: '#/components/schemas/captable.captableTotals' description: Totals is the company-wide share count. type: object captable.captableSummaryCompany: properties: id: description: ID is the company id, which is the tenant's own org id. type: string name: description: Name is the company's legal name. type: string type: object captable.captableTotals: properties: fullyDilutedShares: description: FullyDilutedShares is outstandingShares plus grantedOptions. format: int64 type: integer grantedOptions: description: "GrantedOptions is the shares under non-terminal option grants \u2014 grants that\nare EXERCISED, EXPIRED\ \ or CANCELLED are excluded, so nothing double-counts." format: int64 type: integer outstandingShares: description: OutstandingShares is the sum of every issued share certificate. format: int64 type: integer shareClasses: description: ShareClasses is how many share classes the company has authorized. format: int64 type: integer stakeholders: description: Stakeholders is how many stakeholders the company has. format: int64 type: integer type: object captable.captableTransferred: properties: message: description: Message is the human sentence the cap table wrote, e.g. "Share transferred". type: string newShareId: description: "NewShareID names the certificate a PARTIAL transfer created. It is null on a\nfull transfer, which\ \ reassigns the existing certificate instead of splitting\nit \u2014 so null here means \"no new certificate\"\ , never \"the transfer failed\"." type: string success: description: Success is true when the transfer was applied. type: boolean transferred: description: Transferred is how many shares moved. format: int64 type: integer type: object captable.captableUpdated: properties: message: description: Message is the human sentence the cap table wrote, e.g. "Company updated". type: string success: description: Success is true when the update was applied. type: boolean type: object catalog.Entry: properties: archetype: description: "Archetype is WHAT KIND OF THING this is, from a closed and ordered list \u2014\nmodel | contract |\ \ chain | sdk | template | infra | site | app \u2014 derived from\nthe repository's own topics, name and description,\ \ first match winning, and\nalways `site` for a deployed site. It is DERIVED, never guessed by a model,\nbecause\ \ a wrong archetype hides a row from the browse rail more thoroughly\nthan a missing one does. Empty when no topic\ \ matched: unclassified, not\nuncategorisable." type: string description: description: 'Description is the repository''s own one-line GitHub description, carried verbatim. It comes from the SOURCE half of a row, so a site that was never matched to a repository has none, and nothing here is written by us.' type: string forkable: description: 'Forkable is NOT omitempty: false is an answer here, not a missing field. Omitted, a client could not tell "you cannot fork this" from "nobody said".' type: boolean id: description: 'ID is "/" and is the corpus''s primary key: a re-published entry updates in place under it rather than accumulating duplicates, so it is the one handle stable enough to link to or to name in a `template` filter. Two orgs can spell the same id, and `canonical` picks which one keeps it.' type: string kind: description: repo | site type: string language: description: 'Language is the repository''s primary implementation language as GitHub computes it ("Go", "TypeScript"), and the case is GitHub''s. Empty for a site with no source half and for a repository GitHub could not classify.' type: string license: description: "License is the terms that upstream work carries, in whichever form the half\nthat credited it had:\ \ an SPDX id (\"MIT\", \"Apache-2.0\") on a GitHub fork,\nfree text on a site whose publisher declared it. GitHub's\ \ NOASSERTION \u2014 \"we\ncould not identify it\" \u2014 reads as none rather than as a licence by that name.\n\ So empty means UNDECLARED and never unencumbered, and Upstream is what says\nwhether the question applies at all." type: string name: description: "Name is the short identifier inside the org \u2014 the repository's name, or the\nsite's slug \u2014\ \ and is the half of ID after the slash. Not a display name;\nTitle is." type: string note: description: 'Note is why a row is NOT in the published catalog, set by the admission gate (gate.go) on the sites it holds back. It is the difference between a demo that silently vanished from the public lens and one whose owner can read the reason and fix it. A published row never carries one.' type: string org: description: hanzo | lux | zoo type: string origin: description: 'Origin is WHAT THIS IS TO YOU: template | community | third-party | product (origin.go owns the four nouns and derives them). Not omitempty, for the same reason Forkable is not: every row has an answer, and a missing one is exactly the flattening this field exists to end.' type: string repo: description: source type: string scope: description: 'Scope is provenance, not storage: "public" for a row from the published corpus, "org" for one only this caller can see. A UI that cannot tell them apart cannot warn before sharing a link.' type: string stars: description: "Stars is GitHub's stargazer count for the source repository, read at the last\nsync and never accumulated\ \ here. It is not a ranking \u2014 the page sorts on\nUpdated \u2014 but it is the tiebreak when two orgs claim\ \ one ID. Absent for a\nsite with no repository behind it, and for a repository nobody has starred." format: int64 type: integer template: description: lineage, if forked from one type: string title: description: "Title is what to SHOW. A site's human name wins where it has one; a repo row\nfalls back to the repository\ \ name, so on a repo this usually just repeats\nName. Absent only for a site whose project was never named \u2014\ \ render Name." type: string updated: description: "Updated is when the thing last MOVED, as RFC 3339 in UTC: a repository's last\npush, or a site's last\ \ deploy. The page is ordered on it, most recent first,\nby comparing these strings \u2014 so the format is load-bearing\ \ and not cosmetic.\nAbsent means the source reported no timestamp, and such a row sorts last." type: string upstream: description: "Upstream/License credit the third-party work an entry was published from:\nthe difference between\ \ \"this org built it\" and \"somebody else built it and\nwe are showing it to you\".\n\nWHO built it is Org,\ \ above \u2014 the account that paid for the project. There\nwas once a separate admin-gated `official` boolean\ \ here claiming the same\nthing, and because it was gated it disagreed: apps Hanzo wrote and hosts\nwere published\ \ by a script holding an ordinary org token, so it stayed\nfalse on all of them and this directory filed our own\ \ work as somebody\nelse's. A field that restates an unforgeable fact can only ever be the\nwrong copy of it." type: string url: description: live, if it is deployed type: string type: object catalog.catalogPage: properties: data: description: Data is the page of matching entries, most recently updated first. items: $ref: '#/components/schemas/catalog.Entry' type: array facets: additionalProperties: additionalProperties: format: int64 type: integer type: object description: "Facets counts the whole matching set along every browse axis, so a rail a\nclient renders is a rail\ \ that has results behind it. Keyed axis \u2192 value \u2192 count." type: object total: description: "Total is how many entries matched BEFORE paging \u2014 what a pager sizes itself on." format: int64 type: integer type: object channel.allowlistPutIn: properties: accessGroups: additionalProperties: additionalProperties: items: type: string type: array type: object description: 'AccessGroups REPLACES the org''s named access groups, as group name -> channel -> entries. Absent or null leaves them alone.' type: object channel: description: 'Channel is the transport to edit: discord, github, linear, slack, teams, telegram or whatsapp. Required; an unknown value is a 404.' type: string dm: description: "DM REPLACES the config-managed DM allow entries. Absent or null leaves them\nalone; an empty list\ \ clears them. It never touches senders approved through\npairing \u2014 a policy edit cannot revoke an approved\ \ pairing." items: type: string type: array dmPolicy: description: 'DMPolicy sets how direct messages are admitted: "pairing" (a person must be approved first), "allowlist" (only listed senders) or "open". Empty leaves it unchanged.' type: string group: description: 'Group REPLACES the config-managed group allow entries. Absent or null leaves them alone; an empty list clears them.' items: type: string type: array groupPolicy: description: 'GroupPolicy sets how group and thread rooms are admitted: "open", "allowlist" or "disabled". Empty leaves it unchanged.' type: string type: object channel.allowlistView: properties: accessGroups: additionalProperties: additionalProperties: items: type: string type: array type: object description: 'AccessGroups is the org''s named sender sets, as group name -> channel -> member entries, held once for the whole org. A DM or Group entry written `accessGroup:` admits any sender listed under that name for THIS channel, or under the channel `*`, which is how one set covers every transport at once. Replaced wholesale by the PUT.' type: object dm: description: "DM is the CONFIG-managed DM allow entries \u2014 the list PUT\n/v1/channel/allowlist owns and replaces\ \ wholesale. An entry matches a sender\neither EXACTLY, as the transport-native id inbox messages carry, or as\n\ `accessGroup:` resolved through AccessGroups. A bare `*` admits\neveryone, but only while DMPolicy is \"\ open\": it is gate syntax, not an\nidentity, so under \"allowlist\" it matches nobody." items: type: string type: array dmPolicy: description: "DMPolicy decides every inbound DIRECT message, defaulting to \"pairing\" when\nthe org has never set\ \ one. \"pairing\": a sender with no entry is sent a\npairing code and the message is DROPPED \u2014 it never\ \ reaches the inbox \u2014 and\nthey are admitted only once an admin approves. \"allowlist\": only DM admits,\n\ and Paired senders are suspended, since a pairing grant counts under\n\"pairing\" alone. \"open\" is not unconditional\ \ either \u2014 it still requires `*`\nor a matching entry in DM." type: string group: description: 'Group is the CONFIG-managed group allow entries, consulted only while GroupPolicy is "allowlist". Entries match the same two ways as DM, and here a bare `*` admits every sender in the room.' items: type: string type: array groupPolicy: description: "GroupPolicy decides every inbound GROUP or THREAD message \u2014 a thread is a\ngroup surface \u2014\ \ defaulting to \"open\". \"open\" admits every sender in the room.\n\"allowlist\" admits only what Group lists,\ \ so an EMPTY Group blocks the\nchannel's group rooms outright. \"disabled\" drops all of them." type: string paired: description: "Paired is the senders admitted by PAIRING \u2014 the entries POST\n/v1/channel/pairing/approve minted,\ \ DM scope only. READ-ONLY on this\nendpoint: the PUT writes config entries and can never revoke one of these\n\ (listing a paired sender under DM instead promotes that entry to config,\nwhich the admin then owns). They admit\ \ only while DMPolicy is \"pairing\"." items: type: string type: array type: object channel.approvePairingIn: properties: channel: description: 'Channel is the transport the request came in on: discord, slack, teams, telegram or whatsapp.' type: string code: description: 'Code is the pairing code from GET /v1/channel/pairing. It is a capability: holding it is what authorises the approval, alongside org admin.' type: string type: object channel.capabilities: properties: actions: description: "Actions is whether the transport renders an INTERACTIVE control natively, and\nit is the flag to read\ \ before composing one. The vocabulary is a closed\nkind-tagged union (envelope.go), exactly four kinds, each\ \ carrying only its\nown field plus an optional label:\n\n\tcommand \u2014 a bot command to run (`command`),\ \ rendered as a button that\n\t invokes it.\n\turl \u2014 an external link (`url`), rendered as\ \ a link button.\n\tselect \u2014 a menu (`options`, each a label and the value choosing it\n\t returns),\ \ rendered as a picker.\n\tapproval \u2014 a reference to an approval request (`approval.id`), rendered as\n\t\ \ approve/deny controls bound to that id.\n\nFalse on every transport, and nothing refuses a send for\ \ it:\nactions are accepted, validated per kind, and flattened by renderText to one\nline each after the text\ \ \u2014 `[label] command`, `[label] url`,\n`[label] opt | opt`, `[label] approval requested: `. So a caller\ \ that\nneeds a real control must read this flag and degrade itself; a caller that\nonly needs the choice communicated\ \ can send actions and take the text form." type: boolean dm: description: "DM is whether the transport carries a DIRECT message at all. True for slack,\nteams, telegram and\ \ whatsapp \u2014 whatsapp is nothing else, since the Cloud API\naddresses a person's number and there is no room\ \ a third party joins. False\nfor discord, honestly: that ingress is guild-scoped slash commands \u2014 an\ninteraction\ \ without a guild id is refused at the endpoint \u2014 so nothing ever\narrives classified as a DM, no reply route\ \ is ever learned for one, and a\nsend addressed at a Discord DM is refused 409." type: boolean edit: description: "Edit is whether a sent message can be rewritten in place. True for slack\nalone. It is what lets a\ \ turn keep ONE message \u2014 progress, then the answer \u2014\nand a transport without it shows the answer only." type: boolean group: description: "Group is whether the transport carries multi-person rooms \u2014 a Discord guild\nchannel, a Slack\ \ channel, a Teams channel or group chat, a Telegram group or\nsupergroup. False on whatsapp alone, which has\ \ no such room to carry." type: boolean media: description: "Media is whether the transport renders an ATTACHMENT natively. False\neverywhere, and a send is not\ \ refused for it: renderText flattens each\nattachment to one `kind: url (mime)` line after the text rather than\ \ dropping\nit. A transport whose egress passes the raw text straight through would drop the\nattachment instead,\ \ and an attachment-only send would reach the platform\nwith nothing to say \u2014 which is why the flag and the\ \ flattening are pinned\ntogether." type: boolean thread: description: "Thread is whether a reply can be threaded UNDER a specific message. True for\nslack alone: it is the\ \ only transport whose ingress reports a thread\n(thread_ts, published as the envelope's replyTo) and whose send\ \ posts back\ninto it. Discord's replyTo makes an inline reply rather than a thread,\nTelegram's and WhatsApp's\ \ each quote one message, and Teams carries no reply\ntarget at all \u2014 a replyTo sent to it is ignored." type: boolean type: object channel.channelAgents: properties: channel: description: Channel is the transport these bindings are for. type: string default: description: 'Default is the agent that answers any room without a binding of its own; "hanzo" when the org has never set one.' type: string rooms: additionalProperties: type: string description: Rooms maps a platform room id to the agent that answers there. type: object type: object channel.channelAgentsPut: properties: channel: description: Channel is the transport to edit. Required; an unknown value is a 404. type: string default: description: 'Default sets the agent for rooms with no binding of their own; "hanzo" restores the built-in. Empty or absent leaves it unchanged.' type: string rooms: additionalProperties: type: string description: Rooms binds platform room ids to agents; rooms not named are left alone. type: object unbind: description: Unbind removes the bindings of these rooms, so they fall back to Default. items: type: string type: array type: object channel.channelView: properties: account: description: "Account is the id-shaped fact about that connection: the lowercased external\nid integrations custodies\ \ for it \u2014 a Discord guild id, a Slack team\n(workspace) id, a Teams AAD tenant id, or the Telegram chat\ \ the org bound.\nEmpty when not connected. Informational: the access policy keys on\n(org, channel), so exactly\ \ one account is representable per pair." type: string accountLabel: description: "AccountLabel is the human label of that same account \u2014 the Discord guild\nname, the Slack team\ \ name, the Teams tenant name (falling back to the tenant\nid), the Telegram chat title. DISPLAY ONLY: never a\ \ key, and never swapped\nwith Account, on any surface." type: string capabilities: $ref: '#/components/schemas/channel.capabilities' description: "Capabilities is what this transport renders natively \u2014 read it before\ncomposing a message that\ \ needs threading, media or interactive actions." connected: description: "Connected is whether integrations holds a connection for (this org, this\ntransport) \u2014 whether\ \ someone finished its connect flow. False leaves Account\nand AccountLabel empty, and a send is then refused\ \ downstream rather than\nhere: by the transport's own binding check (403 for a Telegram chat this org\nhas not\ \ bound, 409 for a Discord or Teams room with no inbound-learned\nroute), or on Slack by the absent per-org bot\ \ token, which surfaces as 502." type: boolean dmPolicy: description: 'DMPolicy is how this org admits direct messages here: "pairing", "allowlist" or "open", defaulting to "pairing" when the org has never set one.' type: string groupPolicy: description: "GroupPolicy is how this org admits group and thread rooms here: \"open\",\n\"allowlist\" or \"disabled\"\ , defaulting to \"open\". Both policy fields come\nback EMPTY \u2014 rather than the listing failing \u2014 when\ \ the policy cannot be\nread; GET /v1/channel/allowlist carries the same two with the entries they\nconsult." type: string id: description: "ID is the fixed transport identifier \u2014 discord, slack, teams, telegram or\nwhatsapp \u2014 and\ \ the value every route on this surface names a channel by, including the\n`:channel` segment of the send path.\ \ The listing is always in that order." type: string pendingPairing: description: "PendingPairing counts the org's UNEXPIRED pairing requests on this channel:\nexactly the rows GET\ \ /v1/channel/pairing returns for it, one per person\nwaiting on an admin. It never exceeds three \u2014 the pending\ \ cap per\n(org, channel) \u2014 and expired requests are not counted." format: int64 type: integer type: object channel.chatChannels: properties: channels: description: 'Channels is every chat transport this deployment supports, in a fixed order, each carrying whether the org has connected it, the account behind the connection, what the transport can do, the org''s DM/group access policies for it, and how many pairing requests are waiting.' items: $ref: '#/components/schemas/channel.channelView' type: array type: object channel.inboxPage: properties: cursor: description: 'Cursor is the row id to pass back as `since` for the next page. It is the last message''s id, or the requested cursor when the page is empty.' format: int64 type: integer messages: description: Messages are the inbound messages, oldest first. items: $ref: '#/components/schemas/channel.inboxView' type: array type: object channel.inboxView: properties: account: description: "Account is the lowercased external id of the org's connected account on that\ntransport: the Discord\ \ guild id, the Slack team id, the Teams AAD tenant id,\nor the bound Telegram chat id. Informational only \u2014\ \ the gate keys on\n(org, channel), never on the account." type: string channel: description: "Channel is the transport this message arrived on \u2014 discord, slack, teams or\ntelegram \u2014\ \ and the `:channel` segment to reply through." type: string createdAt: description: "CreatedAt is Unix SECONDS, stamped by the ingest goroutine when the message\nwas accepted \u2014 not\ \ the transport's own send time. Rows are dropped 30 days\nafter it." format: int64 type: integer id: description: "ID is the store's row id, assigned on insert \u2014 SERVER-SET, and the cursor:\npass a page's last\ \ id back as `since`. It rises with arrival order but is\nnot contiguous, because one sequence is shared by every\ \ org in the store and\na caller reads only its own rows." format: int64 type: integer replyTo: description: "ReplyTo is the transport's reply target for this message: Slack's thread_ts,\nor the Telegram message\ \ id it arrived as. Send it back as the body's\n`replyTo` to answer in the SAME thread. Empty means the transport\ \ reported\nnone \u2014 a top-level Slack message, and every Discord and Teams message, since\nneither carries\ \ one \u2014 and a reply then lands at the top level of the room." type: string roomId: description: "RoomID is the conversation on the ORIGINATING transport, and the value to\nsend back as `room.id`:\ \ a Discord channel snowflake, a Slack conversation id\n(D\u2026 IM, C\u2026 public channel, G\u2026 private or\ \ mpim), a Teams conversation id\n(19:\u2026@thread.\u2026 for a channel or group chat, a:\u2026 for a personal\ \ chat), or a\nTelegram chat id in decimal (negative for a group, positive for a DM). It is\nstable for the life\ \ of the room, so every message from one conversation\ncarries the same value." type: string roomKind: description: "RoomKind is how ingest classified the room: \"dm\", \"group\" or \"thread\". It\ndecides which policy\ \ gated the message \u2014 dmPolicy for \"dm\", groupPolicy for\nBOTH \"group\" and \"thread\". Only Slack ever\ \ reports \"thread\"; Telegram's\nreply-to id becomes ReplyTo instead, and Discord's ingress is guild-scoped\n\ so its rooms are always \"group\"." type: string sender: description: "Sender is the TRANSPORT-NATIVE user id of whoever wrote the message \u2014 a\nDiscord member.user.id,\ \ a Slack U\u2026 user id, a Teams aadObjectId (falling\nback to from.id), a Telegram from.id in decimal. Stable\ \ per person per\ntransport, and the identity the gate keys on: an allow entry, an access-group\nmember and a\ \ pairing approval all name exactly this value." type: string senderUser: description: "SenderUser is the HANZO account subject that chat identity is linked to,\nresolved at ingest through\ \ the org's user link. Best-effort and omitted when\nabsent: a person who never linked their chat account \u2014\ \ or a link store that\ncould not be read \u2014 leaves it empty and is never blocked for it." type: string text: description: "Text is the body as the transport delivered it, with the bot mention already\nstripped by the ingress\ \ adapter (on Discord it is the /hanzo prompt argument,\nsince that ingress is slash commands only), truncated\ \ to 8 KiB on store.\nInbound attachments are not stored \u2014 this is the whole of what was said." type: string type: object channel.pairingApproved: properties: ownerBootstrapped: description: 'OwnerBootstrapped is true when this approval was the org''s FIRST on the channel and therefore also made the sender its owner.' type: boolean sender: description: Sender is the external chat identity that is now allowed to DM the org's bot. type: string type: object channel.pairingQueue: properties: pending: description: 'Pending is every unexpired pairing request waiting on an org admin, each carrying the channel, the requesting sender and the code to approve it with.' items: $ref: '#/components/schemas/channel.pairingView' type: array type: object channel.pairingView: properties: channel: description: "Channel is the transport the request arrived on \u2014 discord, slack, teams or\ntelegram \u2014 and\ \ half of what approval names. The cap of three unapproved\nrequests applies per (org, channel); while it is full\ \ no further code is\nminted until one is approved or expires." type: string code: description: "Code is the CAPABILITY that authorises the approval: eight characters from a\n32-symbol uppercase\ \ alphabet (A-Z0-9 minus the confusables 0, O, 1 and I),\nminted with crypto/rand and also sent to the requester\ \ in chat. An org admin\npasses it with the channel to POST /v1/channel/pairing/approve, which\nCONSUMES it \u2014\ \ the request row is deleted, so a code approves once \u2014 and which\ntakes org admin as well as the code. It\ \ lives ONE HOUR from CreatedAt;\nexpired requests are not listed here, and approving one is a 404. It is shown\n\ on this admin surface and NEVER logged." type: string createdAt: description: 'CreatedAt is Unix SECONDS of FIRST contact: when the request was minted and the code sent. Expiry is measured from here and from nowhere else.' format: int64 type: integer lastSeen: description: "LastSeen is Unix SECONDS of the MOST RECENT message from this sender while\nthe request has been pending.\ \ It moves as they keep writing, which is how an\nadmin tells a live request from an abandoned one \u2014 but\ \ it does not extend the\nhour and does not re-send the code, since one request sends exactly one chat\nreply." format: int64 type: integer sender: description: "Sender is the transport-native user id waiting for access \u2014 the same\nidentity inbox messages\ \ carry. Approving mints a DM allow entry for exactly\nthis value and nothing wider: pairing never grants group\ \ access." type: string type: object ci.Artifact: properties: digest: type: string tag: type: string type: object ci.Check: properties: at: format: date-time type: string job: description: 'Job is the job that decided State. A run reports one conclusion for however many jobs it holds, and the jobs are not interchangeable: the pipeline fails at `gate` before it builds anything and at `receipt` after it has already built, pinned and proved the release live. Both read `failure` on the run, and only the first one means nothing shipped.' type: string number: format: int64 type: integer state: description: "State is success | failure | running | absent. `absent` is not a kind of\nfailure and is kept apart\ \ from one: a failing run is a build that ran and\nsaid no, while an absent run is Hanzo Git never having constructed\ \ a run\nfor the commit at all \u2014 a workflow it cannot parse or a reference it\ncannot resolve. There is no\ \ log to open for the second, so a page that\ndraws them the same sends you looking for one that does not exist." type: string tested: description: "Tested reports that the run's tests executed; Verdict reports that the run\nsaid anything about tests\ \ at all. They are separate because the interesting\ncase is a run that passed while its test step was skipped\ \ \u2014 a green build\nthat proved nothing \u2014 and that is invisible if the two are one flag." type: boolean url: type: string verdict: type: boolean type: object ci.Execution: properties: actor: type: string branch: type: string conclusion: type: string endedAt: format: date-time type: string event: type: string id: format: int64 type: integer number: format: int64 type: integer org: type: string repo: type: string sha: type: string startedAt: format: date-time type: string status: description: "Status and Conclusion are BOTH required to know how a run went, and\nreading only one is wrong in\ \ a way that looks fine. Status answers\n\"is it over\" (queued | in_progress | completed); Conclusion answers\n\ \"how did it end\" and is empty until it is over. A view that buckets on\nStatus alone sees `completed` and cannot\ \ tell a pass from a failure, so\nit draws every finished run \u2014 successes and cancellations included \u2014\ \ the\nsame way." type: string title: type: string url: type: string workflow: type: string type: object ci.Executions: properties: fetchedAt: format: date-time type: string orgs: items: type: string type: array repos: format: int64 type: integer runs: items: $ref: '#/components/schemas/ci.Execution' type: array sourceErr: type: string stale: type: boolean type: object ci.Pipeline: properties: behind: description: 'Behind counts commits after the one that last produced an image whose own build has FINISHED without producing one, and Since is when the oldest of them landed. A commit still building is not counted, so a push in flight is not drift and a service appears here only once something has actually stopped without shipping. How long that has stood is the number worth acting on; that it is true says nothing about whether anyone should move.' format: int64 type: integer built: $ref: '#/components/schemas/ci.Artifact' declared: $ref: '#/components/schemas/ci.Artifact' drift: items: type: string type: array head: $ref: '#/components/schemas/ci.Tip' image: description: ghcr.io/hanzoai/cloud type: string name: description: cloud type: string namespace: description: hanzo type: string org: description: Hanzo Git owner; empty when the repo is unresolved type: string pinnedAt: format: date-time type: string ready: format: int64 type: integer repo: description: hanzo-inc/cloud type: string running: $ref: '#/components/schemas/ci.Artifact' since: format: date-time type: string want: format: int64 type: integer type: object ci.Pipelines: properties: fetchedAt: format: date-time type: string orgs: items: type: string type: array services: items: $ref: '#/components/schemas/ci.Pipeline' type: array sourceErr: type: string stale: type: boolean type: object ci.Tip: properties: at: format: date-time type: string build: $ref: '#/components/schemas/ci.Check' sha: type: string title: type: string type: object cloudflare.D1Query: properties: params: description: "Params are the statement's bound values, in the order its `?` placeholders\nappear \u2014 a string,\ \ a number, a boolean or null, whatever the column takes.\nAbsent means the statement carries no placeholders;\ \ bind values here rather\nthan interpolating them into the statement." items: {} type: array sql: description: 'SQL is the statement to run. Blank (or absent) is refused before anything reaches D1.' type: string type: object cloudflare.PagesBuildConfig: properties: build_command: description: 'BuildCommand is what Cloudflare runs to build the site ("npm run build"). Omitted means no build step: the repository is published as it stands.' type: string destination_dir: description: 'DestinationDir is the directory the build leaves the site in ("dist"), relative to RootDir. It is what gets served.' type: string root_dir: description: 'RootDir is where in the repository the build runs, for a project that is not at the repository root. Omitted means the root.' type: string type: object cloudflare.PagesD1Binding: properties: id: description: 'ID is the D1 database this binding points at, by Cloudflare''s uuid. The binding name the Worker code reads it as is the map key, not a field here.' type: string type: object cloudflare.PagesDeploymentConfig: properties: compatibility_date: description: 'CompatibilityDate pins which Workers runtime behaviour the functions run under, as a date ("2024-01-01"). It is a pin, not a version: the runtime keeps that date''s semantics for code deployed against it.' type: string compatibility_flags: description: 'CompatibilityFlags turn individual runtime behaviours on or off ahead of, or behind, the date above ("nodejs_compat").' items: type: string type: array d1_databases: additionalProperties: $ref: '#/components/schemas/cloudflare.PagesD1Binding' description: D1Databases binds D1 databases in, keyed by binding name. type: object env_vars: additionalProperties: $ref: '#/components/schemas/cloudflare.PagesEnvVar' description: 'EnvVars are the environment variables the functions see, KEYED BY VARIABLE NAME. The key is the name; the value carries the value and whether it is a secret.' type: object kv_namespaces: additionalProperties: $ref: '#/components/schemas/cloudflare.PagesKVBinding' description: 'KVNamespaces binds KV namespaces into the functions, KEYED BY THE BINDING NAME the code reads (`env.SESSIONS`). Same shape for the two below.' type: object r2_buckets: additionalProperties: $ref: '#/components/schemas/cloudflare.PagesR2Binding' description: R2Buckets binds R2 buckets in, keyed by binding name. type: object type: object cloudflare.PagesDeploymentConfigs: properties: preview: $ref: '#/components/schemas/cloudflare.PagesDeploymentConfig' description: 'Preview is the config every branch build other than the production branch runs under. It is a SEPARATE set of bindings and variables, which is what lets a preview point at test data.' production: $ref: '#/components/schemas/cloudflare.PagesDeploymentConfig' description: Production is the config the production branch builds under. type: object cloudflare.PagesEnvVar: properties: type: description: "Type is \"plain_text\" or \"secret_text\" and decides that: plain text is readable\nafterwards, secret\ \ text is write-only. Empty is Cloudflare's default,\nplain_text \u2014 so a secret with no type set is stored\ \ in the clear." type: string value: description: 'Value is the variable''s value. Under type "secret_text" Cloudflare encrypts it on arrival and never reads it back, so a later read of the project shows the variable without this.' type: string type: object cloudflare.PagesKVBinding: properties: namespace_id: description: "NamespaceID is the KV namespace this binding points at, by Cloudflare's id\nrather than its title.\ \ The BINDING NAME \u2014 what the Worker code reads it as \u2014 is\nthe map key this value sits under, not a\ \ field here." type: string type: object cloudflare.PagesProjectCreate: properties: build_config: $ref: '#/components/schemas/cloudflare.PagesBuildConfig' description: BuildConfig says how to build the site. Omitted means no build step. deployment_configs: $ref: '#/components/schemas/cloudflare.PagesDeploymentConfigs' description: "DeploymentConfigs carries the preview and production runtime configs \u2014 the\nbindings and variables\ \ the built site's functions run with." name: description: 'Name is the project name, and it is also the address: the site answers at .pages.dev. Cloudflare will not rename a project afterwards.' type: string production_branch: description: 'ProductionBranch is which git branch builds to production; every other branch builds a preview. Omitted leaves Cloudflare''s own default.' type: string type: object cloudflare.PagesR2Binding: properties: name: description: "Name is the R2 bucket this binding points at, by bucket name \u2014 R2 addresses\nbuckets by name\ \ where KV and D1 use ids. The binding name the Worker code reads\nit as is the map key." type: string type: object cloudflare.WorkerScriptPut: properties: bindings: description: "Bindings are the resources the script can reach (KV, D1, R2, secrets, \u2026), in\nCloudflare's own\ \ binding vocabulary, passed through as written: this plane\ndeliberately does not model Cloudflare's shapes.\ \ Absent uploads a script with\nNO bindings, which replaces whatever the previous version had." compatibilityDate: description: 'CompatibilityDate pins which Workers runtime behaviour the script runs under, as a plain calendar date ("2024-01-01"). Absent leaves the account''s own default in force.' type: string compatibilityFlags: description: 'CompatibilityFlags turn individual runtime behaviours on or off around that date ("nodejs_compat"), in Cloudflare''s own flag vocabulary. Absent means the date alone decides.' items: type: string type: array mainModule: description: MainModule is the module file the runtime starts at. Absent means "worker.js". type: string script: description: "Script means two things on this route, and the document says so in both places\nit appears: the PATH\ \ segment names the Worker to publish, and the BODY field\ncarries that Worker's ES-module source \u2014 the code\ \ itself, never a name or a\nURL. A blank or absent source is refused; there is no empty Worker." type: string type: object cloudflare.bucketCreateIn: properties: name: description: Name is the bucket name to create. type: string type: object cloudflare.databaseCreateIn: properties: name: description: Name is the database name to create. type: string type: object cloudflare.domainAddIn: properties: name: description: Name is the custom domain to attach, e.g. "www.acme.com". type: string project: description: Project is the Pages project name, from the path. type: string type: object cloudflare.namespaceCreateIn: properties: title: description: Title is the namespace's display title. Cloudflare mints the id. type: string type: object cloudflare.purgeIn: properties: files: description: "Files purges exactly the listed URLs \u2014 at most 30, Cloudflare's per-request cap." items: type: string type: array purge_everything: description: Everything drops the zone's entire edge cache. type: boolean zone: description: Zone is the 32-hex Cloudflare zone id, from the path. type: string type: object cloudflare.routeCreateIn: properties: pattern: description: Pattern is the URL pattern to bind, e.g. "acme.com/api/*". type: string script: description: 'Script is the Worker script to dispatch to. Omit it to leave the pattern bound to no script, which is how Cloudflare expresses "bypass the Worker here".' type: string zone: description: Zone is the 32-hex Cloudflare zone id, from the path. type: string type: object cloudflare.subdomainSetIn: properties: enabled: description: 'Enabled publishes the script on \n\n\ `data-key` is the publishable key the project mints; `data-product` optionally names the emitting surface. The key\ \ may also ride the src as `?key=` for a host that strips data attributes.\n\nWITHOUT A KEY THE TAG SENDS NOTHING.\ \ A keyless beacon is accepted 200 into $public, a reserved tenant the owning org cannot read \u2014 so silence is\ \ the honest failure, and the tag picks it rather than reporting success into a tenant nobody reads." operationId: get_event_tag.js responses: 2XX: content: application/javascript: schema: format: binary type: string description: Success summary: "The Hanzo event tag \u2014 the one-line install for a surface with no bundler" tags: - event x-app: event x-public: true /v1/event/timeseries: get: description: "Returns the caller org's LLM usage over time as an evenly-spaced series.\nOne point per hour or per day\ \ \u2014 the bucket the window implies, 24h giving hours and\n7d/30d giving days \u2014 carrying requests, total tokens\ \ and spend in cents. Empty\nbuckets are filled with zeros so a client charts a continuous line.\n\nThe org is the\ \ validated principal's \u2014 never a parameter. 401 without a validated\nbearer, 400 on an unknown range, 503 when\ \ the warehouse is unreachable." operationId: get_event_timeseries parameters: - description: "Range is a relative window: a count and a unit \u2014 24h, 7d, 90d, any h or\nd \u2014 or day,\ \ week, month, all. Default 24h. Ignored when both start and\nend are given. An unknown value, or one past the 730-day\ \ horizon, is a 400." example: 30d in: query name: range required: false schema: type: string - description: Start is the inclusive lower bound of a custom window, RFC3339. Requires end. in: query name: start required: false schema: type: string - description: End is the exclusive upper bound of a custom window, RFC3339. Requires start. in: query name: end required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/event.Timeseries' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the caller org's LLM usage over time as an evenly-spaced series. tags: - event x-app: event x-public: true x-tool: true /v1/event/top: get: description: "Returns the caller org's ranked lenses for one window, five of them at once.\nmodels ranks LLM models\ \ by spend and is always real; products ranks commerce orders\nby revenue; topPages ranks requested paths, topReferrers\ \ the external referrer\ndomains (\"(direct)\" for a missing or same-origin one) and topSources the utm_source\ncampaigns\ \ (\"(none)\" when absent), each by pageviews. Every lens carries each row's\nshare of the in-window total, so a top-N\ \ honestly shows the long tail.\n\nThe four event lenses report available=false rather than fabricating zeros when\ \ the\nproduct-event table holds nothing yet. The org is the validated principal's \u2014 never\na parameter. 401\ \ without a validated bearer, 400 on an unknown range, 503 when the\nwarehouse is unreachable." operationId: get_event_top parameters: - description: "Range is a relative window: a count and a unit \u2014 24h, 7d, 90d, any h or\nd \u2014 or day,\ \ week, month, all. Default 24h. Ignored when both start and\nend are given. An unknown value, or one past the 730-day\ \ horizon, is a 400." example: 7d in: query name: range required: false schema: type: string - description: Start is the inclusive lower bound of a custom window, RFC3339. Requires end. in: query name: start required: false schema: type: string - description: End is the exclusive upper bound of a custom window, RFC3339. Requires start. in: query name: end required: false schema: type: string - description: 'Limit bounds every ranked lens in the response. Default 10, maximum 100; a value at or below zero, or one that is not a number, takes the default.' example: 25 in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/event.Top' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the caller org's ranked lenses for one window, five of them at once. tags: - event x-app: event x-public: true x-tool: true /v1/event/{project}/envelope: post: description: "Accepts the CURRENT Sentry wire \u2014 the framed envelope a modern SDK posts, carrying its items in one\ \ request \u2014 so an application already instrumented with Sentry reports into Hanzo's error tracking by pointing\ \ its DSN here and changing nothing else.\n\nCLOUD ROUTES IT AND READS NONE OF IT. The body is relayed byte-for-byte\ \ to the observability plane, which parses the wire, verifies the credential and answers; this endpoint declares no\ \ response shape because it does not know one. A deployment with no observability plane mounted answers 503.\n\nTHE\ \ CREDENTIAL IS A SENTRY DSN KEY, NOT A HANZO PRINCIPAL. This is one of the few writes on the platform that carries\ \ no bearer and no org header by design \u2014 a Sentry SDK has neither \u2014 and it is exempt from the principal\ \ gate for that reason. The observability plane verifies the DSN key itself, fail-closed: a request without a valid\ \ one is refused there, never admitted here. Presenting a Hanzo bearer instead does nothing.\n\n`project` IS THE DSN'S\ \ PROJECT ID \u2014 the identifier in the DSN the SDK was configured with, and what the tenant is derived from. It\ \ is NOT a Hanzo IAM project and NOT a todo project key. Only these two ingest paths map through: no observability\ \ READ API is reachable by any other suffix under this prefix." operationId: post_event_by_project_envelope parameters: - in: path name: project required: true schema: type: string requestBody: content: application/octet-stream: schema: format: binary type: string summary: "Sentry SDK envelope ingest \u2014 errors and traces from an unmodified Sentry client" tags: - event x-app: event x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/event/{project}/store: post: description: "Accepts the LEGACY Sentry wire: one event per request, what an SDK predating envelopes sends. Same handler,\ \ same credential, same destination as the envelope endpoint \u2014 kept open so an old client reports without being\ \ upgraded first. New instrumentation has no reason to choose it.\n\nCLOUD ROUTES IT AND READS NONE OF IT. The body\ \ is relayed byte-for-byte to the observability plane, which parses the wire, verifies the credential and answers;\ \ this endpoint declares no response shape because it does not know one. A deployment with no observability plane\ \ mounted answers 503.\n\nTHE CREDENTIAL IS A SENTRY DSN KEY, NOT A HANZO PRINCIPAL. This is one of the few writes\ \ on the platform that carries no bearer and no org header by design \u2014 a Sentry SDK has neither \u2014 and it\ \ is exempt from the principal gate for that reason. The observability plane verifies the DSN key itself, fail-closed:\ \ a request without a valid one is refused there, never admitted here. Presenting a Hanzo bearer instead does nothing.\n\ \n`project` IS THE DSN'S PROJECT ID \u2014 the identifier in the DSN the SDK was configured with, and what the tenant\ \ is derived from. It is NOT a Hanzo IAM project and NOT a todo project key. Only these two ingest paths map through:\ \ no observability READ API is reachable by any other suffix under this prefix." operationId: post_event_by_project_store parameters: - in: path name: project required: true schema: type: string requestBody: content: application/octet-stream: schema: format: binary type: string summary: "Sentry SDK store ingest \u2014 the legacy single-event wire" tags: - event x-app: event x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/exec: post: description: "Executes a program in a throwaway sandbox and answers with what it printed\nand what it left behind.\n\ \n`lang` names one of the thirteen the sandbox image carries \u2014 py, js, ts, bash, r,\nphp, go, rs, c, cpp, java,\ \ d, f90 \u2014 and `code` is the whole program, not a\nfragment: a compiled language is compiled and then run, an\ \ interpreted one is\ninterpreted, and `args` becomes the program's own argv either way. Nothing is\ninstalled for\ \ you; the image is the environment.\n\nA PROGRAM THAT FAILS IS A SUCCESSFUL CALL. A non-zero exit answers 200 with\ \ the\ndiagnostics on `stderr`, because \"the code threw\" and \"the interpreter is down\"\nare different facts a\ \ caller renders differently. Only the second is an error\nstatus.\n\nRuns are stateful through `session_id`. Omit\ \ it and the run gets a fresh sandbox\nwhose id comes back on the answer; pass that id again and the next run sees\ \ the\nsame filesystem, so a program can write a file one call and read it the next.\n`files` names bytes already\ \ uploaded to a session (POST /v1/exec/upload), copied in\nbefore the program starts. `files` on the ANSWER is what\ \ the program created or\nchanged, by comparison against a marker taken at start \u2014 so it is the run's real\n\ output, not a listing of the directory \u2014 and each is fetched from\nGET /v1/exec/download/{session}/{name}.\n\n\ The tenant is the caller's, never the body's, at every entry point. A typed op is\nalso an MCP tool and an op-plane\ \ op; MCP's tools/call invokes it directly, with no\nroute and therefore no middleware, so nothing there could have\ \ checked a\ncredential. tenantOf refuses a context carrying no validated principal, so those\nentry points fail closed\ \ without a second gate to keep in step." operationId: post_exec requestBody: content: application/json: schema: $ref: '#/components/schemas/exec.CodeRun' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/exec.CodeResult' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Run a code snippet in a sandboxed interpreter tags: - exec x-app: exec x-public: true x-tool: true /v1/exec/files/{sid}: get: description: "Lists the files in an execution session.\n\nEverything the session's sandbox holds \u2014 the uploads\ \ a run can read and the\nartifacts it produced \u2014 each then fetched from GET /v1/exec/download.\n\nThe answer\ \ is a BARE JSON ARRAY of {name, lastModified}, where `name` is the\nsame {session_id}/{fileId} identifier download\ \ takes, because that is what the\nclient matches on. The obvious typed shape, `{files: [\u2026]}`, would have been\ \ a\nsilent wire change: the request still succeeds and `response.data.find(...)`\nfinds nothing, which reads as a\ \ session holding no files.\n\nThe NAME of this handler is what the published summary is cut from, and it used\nto\ \ leak: the comment opened \"Files lists \u2026\", which is not this function's\nidentifier, so zipdoc's exact-match\ \ strip left it and every SDK, tool list and\nCLI help line opened with a Go symbol no caller can see. An openapi.Describe\n\ stated a better summary beside the route and was DISCARDED \u2014 Fold replaces a\nstructural operation with the typed\ \ one \u2014 so the declaration read as landed and\nrendered nowhere. The comment is the one home for this sentence.\n\ \nOne recursive `find`, the same traversal the artifact sweep makes. It used to be\n`ls -1A` \u2014 top level only\ \ \u2014 while the sweep collected with `find`, so a run that\nwrote a nested artifact reported it in its reply and\ \ then omitted it here, and\nthe client's prefix match read the file as expired. Two traversals of one\ndirectory\ \ is two answers about what a session holds; there is one now." operationId: get_exec_files_by_sid parameters: - description: "SID is the session identifier \u2014 the sandbox this listing is of. The URL is\nthe addressing authority:\ \ a path segment binds after the body and after the\nquery, so the address decides which session is read whatever\ \ else is sent." in: path name: sid required: true schema: type: string responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/exec.listing' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the files in an execution session. tags: - exec x-app: exec x-public: true x-tool: true /v1/exec/upload: post: description: 'Takes a multipart upload and writes the file into the session''s sandbox, so a later run can read it. Answers the session id and the identifier the file is addressed by; `session_id` in the form joins an existing session instead of opening one. The body is multipart/form-data, which is why this is not a typed operation: every non-empty typed body is decoded as JSON.' operationId: post_exec_upload summary: Upload a file into an execution session tags: - exec x-app: exec x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/experiment: get: description: "Is every experiment in the caller's org, with its variants, status and\ndecision, ordered by project then\ \ id.\n\nScoped to the org resolved from the validated principal \u2014 a distinct org is a\ndistinct physical store,\ \ so no query here can reach another tenant's rows \u2014 and\nfurther narrowed to the caller's project scope when\ \ the credential carries one.\nA principal with NO project scope sees the org's experiments across all of its\nprojects,\ \ which is the answer a reader most often expects to be filtered and is\nnot.\n\nRequires a validated principal; refuses\ \ without one rather than answering an\nempty list." operationId: get_experiment responses: '200': content: application/json: schema: $ref: '#/components/schemas/experiment.experimentList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Is every experiment in the caller's org, with its variants, status and decision, ordered by project then id. tags: - experiment x-app: experiment x-public: true x-tool: true post: description: "Registers a controlled experiment AND puts its assignment flag live, in\nthat order, so the arms start\ \ bucketing subjects the moment this returns 201 \u2014\nthe flag is created active at 100% rollout, with each variant\ \ weighted as\ndeclared. There is no separate start call; creating IS starting.\n\nA variant carries an opaque payload\ \ this primitive never interprets: a feature\nconfig, an ad-creative id, a subject line, a model id.\n\nRequires a\ \ validated principal, and refuses without one. The org and project are\ntaken from that principal and the creator\ \ is stamped from the credential \u2014 none\nof the three is a body field, so an experiment cannot be filed against\ \ another\ntenant. An id already used in this project is a conflict, never a silent\noverwrite: re-creating would\ \ stomp the assignment flag of a run in progress.\n\nIt fails closed on the flag write. An experiment whose assignment\ \ flag does not\nexist would assign nobody, so if that write fails nothing is registered." operationId: post_experiment requestBody: content: application/json: schema: $ref: '#/components/schemas/experiment.createBody' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/experiment.Trial' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Registers a controlled experiment AND puts its assignment flag live, in that order, so the arms start bucketing\ \ subjects the moment this returns 201 \u2014 the flag is created active at 100% rollout, with each variant weighted\ \ as declared." tags: - experiment x-app: experiment x-public: true x-tool: true /v1/experiment/health: get: description: "Is whether the experiments subsystem is mounted and serving in this\nprocess.\n\nIt answers unconditionally.\ \ It proves exactly one thing \u2014 that this binary\nregistered the experiments routes and is dispatching them \u2014\ \ and deliberately no\nmore: it reads no principal, opens no per-org registry, and touches neither the\nflags engine\ \ nor the analytics plane, so a 200 here says nothing about whether a\ngiven tenant's store will open or whether an\ \ analysis can run. It is the only\nroute on this surface that needs no org.\n\nThe static path is registered ahead\ \ of the /:id read, so it always wins the\nfirst-match scan. \"health\" is a legal experiment id, which means an experiment\n\ created under that id can never be fetched by id \u2014 pick another." operationId: get_experiment_health responses: '200': content: application/json: schema: $ref: '#/components/schemas/experiment.health' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Is whether the experiments subsystem is mounted and serving in this process. tags: - experiment x-app: experiment x-public: true x-tool: true /v1/experiment/{id}: get: description: "Is one experiment's definition and lifecycle: variants, weights, control\narm, status and winner.\n\n\ It reads the registry row only \u2014 the definition and the decision, never live\nmeasurements. Assignment lives\ \ in the flags plane and outcomes in analytics;\nthis is the value that names both.\n\nScoped to the caller's org\ \ and project from the validated principal, so another\ntenant's experiment of the same id is simply not found. An\ \ id that is not a\nlegal slug is answered the same way, without a store read \u2014 the shape check and\nthe existence\ \ check are one answer, so neither leaks the other." operationId: get_experiment_by_id parameters: - description: ID is the experiment the URL names. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/experiment.Trial' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Is one experiment''s definition and lifecycle: variants, weights, control arm, status and winner.' tags: - experiment x-app: experiment x-public: true x-tool: true /v1/experiment/{id}/analyze: post: description: "Is per-variant conversion, lift and statistical significance against\nthe control arm.\n\nIt reads per-subject\ \ outcomes from the analytics plane over a window, folds them\ninto per-variant samples, and returns each arm's exposed\ \ count, conversions,\nrate, lift versus control, two-proportion z, two-tailed p-value and whether it\nclears alpha.\ \ Arms with no data still appear with zero exposed, so the read is\ncomplete over the experiment's declared arms;\ \ the control arm sorts first. The\npooled-variance estimator is used and the p-value is exact; a degenerate\ncomparison\ \ (an empty arm, no variance) answers z 0 and p 1 \u2014 not significant,\nnever an error.\n\nOnly EXPOSED subjects\ \ are counted, and each is joined to its arm by re-evaluating\nthe assignment flag AT ANALYSIS TIME \u2014 not from\ \ what was in force during the\nwindow. That is the one rule to get right: analyzing an experiment after its\nwinner\ \ has been promoted re-buckets every subject into the promoted arm,\ncollapsing the control to zero exposed and making\ \ the result meaningless. Read\nthe analysis before deciding. A subject the flag cannot place is dropped rather\n\ than allowed to poison the fold.\n\nThe winner in the response is ADVISORY \u2014 the significant, control-beating\ \ arm\nwith the highest rate, or empty when inconclusive. It promotes nothing; the\ndecision is a separate, explicit\ \ act.\n\nEvery plane read is scoped to the caller's org. Per-variant samples are also\nwritten to the research evidence\ \ plane as immutable ab rows, best-effort: the\nanalysis is still returned if that write fails, because the samples\ \ are\nrecomputable, and the failure is logged rather than swallowed." operationId: post_experiment_by_id_analyze parameters: - description: ID is the experiment the URL names. in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/experiment.analyzeQuery' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/experiment.Analysis' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Is per-variant conversion, lift and statistical significance against the control arm. tags: - experiment x-app: experiment x-public: true x-tool: true /v1/experiment/{id}/assign: get: description: "Is the variant one subject is bucketed into, and the payload that\nvariant carries.\n\nThe bucketing is\ \ a deterministic hash of the subject, so the same subject gets\nthe same arm on every call for as long as the flag\ \ definition is unchanged \u2014\nand this is a pure READ: it records nothing. In particular it does NOT record an\n\ exposure. The caller's SDK must emit the experiment's exposure event itself, or\nthe analysis has an empty denominator\ \ and every arm measures zero.\n\nAn empty variant with on false is not an error \u2014 it means the flag returned\n\ nothing for this subject, so the subject is not enrolled. A flags engine that is\nunavailable refuses rather than\ \ defaulting to an arm. Requires a validated\nprincipal, and the experiment must exist in the caller's org and project." operationId: get_experiment_by_id_assign parameters: - description: ID is the experiment the URL names. in: path name: id required: true schema: type: string - description: "Subject is the unit to bucket \u2014 a user, org, session or audience key,\nmatching the experiment's\ \ subjectKind." in: query name: subject required: true schema: type: string - description: 'Props is a JSON object of person properties for targeting. A value that is not valid JSON is dropped rather than refused, so a malformed one changes the bucketing without saying so.' in: query name: props required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/experiment.assignment' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Is the variant one subject is bucketed into, and the payload that variant carries. tags: - experiment x-app: experiment x-public: true x-tool: true /v1/experiment/{id}/decide: post: description: "Promotes one variant to the whole rollout and records who decided.\n\nIt rewrites the assignment flag\ \ so the named winner serves 100% of the rollout\nand every other arm 0%, preserving the flag's targeting groups and\ \ payloads,\nthen stamps the experiment decided with the winner, the deciding credential and\nthe time. This is a\ \ production behaviour change that takes effect immediately\nfor every subject the flag evaluates.\n\nIt requires\ \ an ORG ADMIN of the caller's own org \u2014 a stricter gate than the rest\nof this surface, matching the flags write\ \ plane, because promoting is a flag\nwrite. The admin check runs AFTER the experiment is found, so a caller from\n\ another tenant is answered not-found rather than forbidden and learns nothing\nabout what exists.\n\nAn experiment\ \ whose assignment flag has gone missing is a conflict rather than a\nsilent no-op \u2014 there is nothing to promote.\n\ \nDeciding is NOT terminal. A second call re-promotes a different variant and\nre-stamps the row; the status stays\ \ decided and the previous winner is\noverwritten with no record that it was ever chosen. Nothing here reverts the\n\ flag to its original weights either, so an experiment cannot be un-decided\nthrough this route \u2014 restoring a\ \ split means writing the flag definition back\nthrough the flags plane." operationId: post_experiment_by_id_decide parameters: - in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/experiment.decideBody' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/experiment.Trial' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Promotes one variant to the whole rollout and records who decided. tags: - experiment x-app: experiment x-public: true x-tool: true /v1/explorer/indexers: get: description: 'Reports the deployment''s chain indexer(s) and how far each has indexed. Identity and health come from the indexer''s /health; the latest indexed block (height + time) from its /v1/explorer/blocks. The row EXISTS if EITHER call reaches the indexer; when the indexer is entirely unreachable the answer degrades to an honest-EMPTY list at 200, not a 502. No chain HEAD is exposed by the indexer REST, so `lag` is honestly omitted rather than fabricated.' operationId: get_explorer_indexers responses: '200': content: application/json: schema: $ref: '#/components/schemas/explorer.indexersOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports the deployment's chain indexer(s) and how far each has indexed. tags: - explorer x-app: explorer x-public: true x-tool: true /v1/explorer/oracles: get: description: 'Reports the on-chain price/data oracles from the graph''s O-Chain PriceFeed registry. A reachable graph with no feeds answers an honest empty list; an unreachable or erroring graph likewise degrades to an empty list at 200 rather than a 502, so the console never error-toasts. No feed is ever fabricated.' operationId: get_explorer_oracles responses: '200': content: application/json: schema: $ref: '#/components/schemas/explorer.oraclesOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports the on-chain price/data oracles from the graph's O-Chain PriceFeed registry. tags: - explorer x-app: explorer x-public: true x-tool: true /v1/flag: post: description: "Runs the caller's flag definitions for one identity and returns the\nflag verdict: which flags are on\ \ (or which variant), their payloads,\nand whether any definition failed to compute. Evaluation is in-process over\ \ the\ncaller's own (org, project) definitions \u2014 no network hop, no shared KV \u2014 so a\ntenant can only ever\ \ evaluate its own flags." operationId: post_flag requestBody: content: application/json: example: distinct_id: u1 groups: '0': key: acme person_properties: plan: pro schema: $ref: '#/components/schemas/flag.evaluateIn' required: true responses: '200': content: application/json: example: errorsWhileComputingFlags: false featureFlagPayloads: {} featureFlags: new-editor: true schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Runs the caller''s flag definitions for one identity and returns the flag verdict: which flags are on (or which variant), their payloads, and whether any definition failed to compute.' tags: - flag x-app: flag x-public: true x-tool: true /v1/flag/activity: get: description: 'Returns the caller''s flag change log newest-first: every create, update and delete, with the actor and the time.' operationId: get_flag_activity parameters: - description: "Limit caps the rows returned. 1\u2013500; anything else takes the default 100." in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/flag.activityOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the caller''s flag change log newest-first: every create, update and delete, with the actor and the time.' tags: - flag x-app: flag x-public: true x-tool: true /v1/flag/decide: post: description: "Runs the caller's flag definitions for one identity and returns the\nflag verdict: which flags are on\ \ (or which variant), their payloads,\nand whether any definition failed to compute. Evaluation is in-process over\ \ the\ncaller's own (org, project) definitions \u2014 no network hop, no shared KV \u2014 so a\ntenant can only ever\ \ evaluate its own flags." operationId: post_flag_decide requestBody: content: application/json: example: distinct_id: u1 groups: '0': key: acme person_properties: plan: pro schema: $ref: '#/components/schemas/flag.evaluateIn' required: true responses: '200': content: application/json: example: errorsWhileComputingFlags: false featureFlagPayloads: {} featureFlags: new-editor: true schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Runs the caller''s flag definitions for one identity and returns the flag verdict: which flags are on (or which variant), their payloads, and whether any definition failed to compute.' tags: - flag x-app: flag x-public: true x-tool: true /v1/flag/defs: get: description: 'Returns every flag definition in the caller''s (org, project) store, by key, with its version and who last changed it.' operationId: get_flag_defs responses: '200': content: application/json: schema: $ref: '#/components/schemas/flag.defsOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns every flag definition in the caller's (org, project) store, by key, with its version and who last changed it. tags: - flag x-app: flag x-public: true x-tool: true /v1/flag/defs/{key}: delete: description: 'Removes one flag definition by key and records the deletion in the change log. A key the caller''s store does not hold is a 404.' operationId: delete_flag_defs_by_key parameters: - description: Key is the flag key to act on, from the path. in: path name: key required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/flag.deletedOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one flag definition by key and records the deletion in the change log. tags: - flag x-app: flag x-public: true x-tool: true get: description: 'Returns one flag definition by key, or 404 when the caller''s store has none under that key.' operationId: get_flag_defs_by_key parameters: - description: Key is the flag key to act on, from the path. in: path name: key required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/flag.DefRow' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one flag definition by key, or 404 when the caller's store has none under that key. tags: - flag x-app: flag x-public: true x-tool: true put: description: "Creates or replaces the flag definition at the path's key and\nreturns the stored row. The BODY IS THE\ \ DEFINITION DOCUMENT \u2014 the flag-definition\nJSON object the evaluator consumes \u2014 and it is stored verbatim\ \ except that its\n\"key\" is forced to the key in the URL, so a document can never be filed under a\nname other than\ \ the one it was addressed by. Every write bumps the version and\nappends to the change log under the caller's identity." operationId: put_flag_defs_by_key parameters: - description: Key is the flag key to write, from the path. example: new-editor in: path name: key required: true schema: type: string requestBody: content: application/json: example: active: true filters: groups: - rollout_percentage: 25 key: new-editor schema: {} required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/flag.DefRow' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates or replaces the flag definition at the path's key and returns the stored row. tags: - flag x-app: flag x-public: true x-tool: true /v1/flag/health: get: description: 'Reports that the flag engine is serving. It is not gated: liveness must be probe-able without a token.' operationId: get_flag_health responses: '200': content: application/json: example: engine: hanzo-flags ok: true schema: $ref: '#/components/schemas/flag.healthOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports that the flag engine is serving. tags: - flag x-app: flag x-public: true x-tool: true /v1/flow/runs: get: description: "Reads one workflow's recorded runs: every component build with its\nresult, keyed by component. Ownership\ \ is verified first \u2014 run records never\ncross the org boundary." operationId: get_flow_runs parameters: - description: 'Workflow is the UUID of the workflow whose run records to read. It rides the query string.' in: query name: workflow required: false schema: type: string responses: '200': content: application/json: schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Reads one workflow''s recorded runs: every component build with its result, keyed by component.' tags: - flow x-app: flow x-public: true x-tool: true post: description: 'Executes one of the caller''s workflows synchronously: the graph runs in the flow service and the response carries the run''s session and outputs. A graph whose components fail reports the product''s own error. Runs are bounded by the product''s five-minute sync ceiling.' operationId: post_flow_runs requestBody: content: application/json: example: input: summarize today's tickets workflow: "8f14e45f-\u2026" schema: $ref: '#/components/schemas/flow.flowRun' required: true responses: '200': content: application/json: schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Executes one of the caller''s workflows synchronously: the graph runs in the flow service and the response carries the run''s session and outputs.' tags: - flow x-app: flow x-public: true x-tool: true /v1/flow/status: get: description: "Reports whether the flow service is reachable and which version it\nruns. It is the product's own /health\ \ and /v1/version composed \u2014 an honest\nlens for \"is the workflow plane up\", never a fabricated ok." operationId: get_flow_status responses: '200': content: application/json: schema: $ref: '#/components/schemas/flow.flowStatus' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports whether the flow service is reachable and which version it runs. tags: - flow x-app: flow x-public: true x-tool: true /v1/flow/workflows: get: description: "Lists the caller's workflows, paged. The list is scoped\nserver-side to the org's project \u2014 the page\ \ can only ever hold the caller's\nown workflows." operationId: get_flow_workflows parameters: - description: Page is the 1-based page of workflows to return. in: query name: page required: false schema: type: string - description: Size is how many workflows one page holds (the product caps it at 100). in: query name: size required: false schema: type: string responses: '200': content: application/json: schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller's workflows, paged. tags: - flow x-app: flow x-public: true x-tool: true post: description: "Creates a workflow in the caller's org. The org's project id\nis pinned server-side from the validated\ \ principal \u2014 there is no field by\nwhich a caller could place a workflow in another org." operationId: post_flow_workflows requestBody: content: application/json: example: description: route tickets name: support-triage schema: $ref: '#/components/schemas/flow.flowCreate' required: true responses: '200': content: application/json: schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a workflow in the caller's org. tags: - flow x-app: flow x-public: true x-tool: true /v1/flow/workflows/{workflow}: delete: description: 'Deletes one of the caller''s workflows and its runs. Ownership is verified first; a foreign id answers 404 and deletes nothing.' operationId: delete_flow_workflows_by_workflow parameters: - description: Workflow is the workflow's UUID, taken from the path. in: path name: workflow required: true schema: type: string responses: '200': content: application/json: schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes one of the caller's workflows and its runs. tags: - flow x-app: flow x-public: true x-tool: true get: description: "Reads one of the caller's workflows \u2014 the full record, graph\nincluded. A workflow outside the caller's\ \ org answers 404, indistinguishable\nfrom one that does not exist." operationId: get_flow_workflows_by_workflow parameters: - description: Workflow is the workflow's UUID, taken from the path. in: path name: workflow required: true schema: type: string responses: '200': content: application/json: schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Reads one of the caller's workflows \u2014 the full record, graph included." tags: - flow x-app: flow x-public: true x-tool: true patch: description: "Patches one of the caller's workflows: name, description,\ngraph, or the locked flag \u2014 only the stated\ \ fields move. Ownership is\nverified before the patch reaches the product." operationId: patch_flow_workflows_by_workflow parameters: - description: Workflow is the workflow's UUID, taken from the path. in: path name: workflow required: true schema: type: string requestBody: content: application/json: example: description: route tickets to the right queue schema: $ref: '#/components/schemas/flow.flowUpdate' required: true responses: '200': content: application/json: schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Patches one of the caller's workflows: name, description, graph, or the locked flag \u2014 only the stated\ \ fields move." tags: - flow x-app: flow x-public: true x-tool: true /v1/framework/doctypes: get: description: 'Returns every DocType defined in the caller''s org. Another tenant''s definitions are never included: the org is part of the store key.' operationId: get_framework_doctypes responses: '200': content: application/json: schema: $ref: '#/components/schemas/framework.docTypeList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns every DocType defined in the caller's org. tags: - framework x-app: framework x-public: true x-tool: true post: description: "Defines a DocType in the caller's org: the metadata that gives a\ndocument surface its fields, its naming\ \ rule, whether it has a submit/cancel\nlifecycle, and which role may do what to it. Manager-only \u2014 on a fresh\ \ org the\nfirst caller to administer it is seeded as its System Manager, after which\nonly a System Manager (or a\ \ platform admin) may define. Answers 201." operationId: post_framework_doctypes requestBody: content: application/json: example: autoname: TASK-.##### fields: - fieldname: subject fieldtype: Data reqd: true module: Projects name: Task schema: $ref: '#/components/schemas/framework.DocType' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/framework.DocType' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Defines a DocType in the caller''s org: the metadata that gives a document surface its fields, its naming rule, whether it has a submit/cancel lifecycle, and which role may do what to it.' tags: - framework x-app: framework x-public: true x-tool: true /v1/framework/doctypes/{name}: delete: description: "Removes a DocType and every document stored under it. The\ndefinition and its data go together \u2014\ \ a document with no schema can be neither\nvalidated nor read back \u2014 so there is no undo. Manager-only. Answers\ \ 204." operationId: delete_framework_doctypes_by_name parameters: - description: "Name is the DocType's ADDRESS \u2014 \"module.name\", e.g. \"kb.page\". A name\ncontaining a space (\"\ erp.Sales Invoice\") arrives percent-encoded and is\ndecoded before it is matched against the stored one." example: Projects.Task in: path name: name required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a DocType and every document stored under it. tags: - framework x-app: framework x-public: true x-tool: true get: description: "Returns one DocType definition \u2014 its fields, naming rule,\npermissions and lifecycle flags. Scoped\ \ to the caller's org, so another\ntenant's DocType of the same name is simply not found." operationId: get_framework_doctypes_by_name parameters: - description: "Name is the DocType's ADDRESS \u2014 \"module.name\", e.g. \"kb.page\". A name\ncontaining a space (\"\ erp.Sales Invoice\") arrives percent-encoded and is\ndecoded before it is matched against the stored one." example: Projects.Task in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/framework.DocType' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one DocType definition \u2014 its fields, naming rule, permissions and lifecycle flags." tags: - framework x-app: framework x-public: true x-tool: true put: description: 'Replaces a DocType definition wholesale (PUT semantics): the stored definition becomes the body. The name in the URL is authoritative over the body''s, and documents already stored under the DocType are left intact. Manager-only.' operationId: put_framework_doctypes_by_name parameters: - example: Projects.Task in: path name: name required: true schema: type: string requestBody: content: application/json: example: fields: - fieldname: subject fieldtype: Data name: Projects.Task schema: $ref: '#/components/schemas/framework.DocType' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/framework.DocType' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Replaces a DocType definition wholesale (PUT semantics): the stored definition becomes the body.' tags: - framework x-app: framework x-public: true x-tool: true /v1/framework/modules: get: description: 'Returns every app lane compiled into this deployment and the DocTypes each one installs. It describes the BINARY, not the org: what a given org has actually installed is the per-module state below.' operationId: get_framework_modules responses: '200': content: application/json: schema: $ref: '#/components/schemas/framework.moduleList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns every app lane compiled into this deployment and the DocTypes each one installs. tags: - framework x-app: framework x-public: true x-tool: true /v1/framework/modules/{module}: get: description: 'Returns one app lane''s install state for the caller''s org: the DocTypes the lane declares, and which of them already exist in the org. That is the honest "set up" versus "installed" answer a console renders.' operationId: get_framework_modules_by_module parameters: - description: Module is the lane's registered name ("cms", "erp"), from the path. example: cms in: path name: module required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/framework.ModuleState' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one app lane''s install state for the caller''s org: the DocTypes the lane declares, and which of them already exist in the org.' tags: - framework x-app: framework x-public: true x-tool: true /v1/framework/modules/{module}/install: post: description: 'Creates an app lane''s DocTypes in the caller''s org. Idempotent and create-if-absent: a DocType the org already has is reported as existing and never replaced, so re-installing cannot clobber a definition the org has since edited. Manager-only.' operationId: post_framework_modules_by_module_install parameters: - description: Module is the lane's registered name ("cms", "erp"), from the path. example: cms in: path name: module required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/framework.Install' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates an app lane's DocTypes in the caller's org. tags: - framework x-app: framework x-public: true x-tool: true /v1/framework/summary: get: description: 'Reports how much of the DocType surface the caller''s org uses: how many DocTypes it has defined, and how many documents exist across them.' operationId: get_framework_summary responses: '200': content: application/json: schema: $ref: '#/components/schemas/framework.summaryView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Reports how much of the DocType surface the caller''s org uses: how many DocTypes it has defined, and how many documents exist across them.' tags: - framework x-app: framework x-public: true x-tool: true /v1/framework/{doctype}: get: description: "Returns the caller org's documents of one DocType, filtered,\nordered and projected by the query. The\ \ DocType is resolved FIRST \u2014 through\nthe same permission gate the list itself uses \u2014 because the query\ \ is validated\nagainst its schema: a filter, sort or field name the DocType does not declare\nis refused rather than\ \ reaching the store." operationId: get_framework_by_doctype parameters: - description: "DocType is the DocType to list, by ADDRESS \u2014 \"module.name\", from the path." example: Projects.Task in: path name: doctype required: true schema: type: string - description: 'Filters is a JSON object of equality matches, e.g. {"priority":"High"}. Every key must be a field the DocType declares (or the managed name / docstatus); an undeclared one is refused rather than silently ignored.' example: '{"priority":"High"}' in: query name: filters required: false schema: type: string - description: "Fields projects the response to a subset \u2014 a JSON array [\"a\",\"b\"] or a\ncomma list \"a,b\"\ . The envelope keys are always returned." in: query name: fields required: false schema: type: string - description: OrderBy is " [asc|desc]". Empty means most-recently-updated first. example: estimate asc in: query name: order_by required: false schema: type: string - description: 'Limit caps the rows returned. Anything that is not a positive integer leaves the engine''s default in place.' example: '20' in: query name: limit required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/framework.documentList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the caller org's documents of one DocType, filtered, ordered and projected by the query. tags: - framework x-app: framework x-public: true x-tool: true post: description: "The body is the DOCUMENT'S field data: a flat JSON object whose properties are the fieldnames the DocType\ \ declares, not a fixed envelope. That is why this operation publishes no request schema \u2014 the shape is metadata\ \ the DocType defines at run time, and no Go struct both accepts it verbatim and describes it, so nothing is asserted\ \ rather than something false.\n\nThe engine validates and coerces every field against the DocType, runs the before_insert\ \ and before_save hooks (either may reject the write), stores the document, then runs the after hooks. It answers\ \ 201 with the stored document: the field data plus the managed envelope \u2014 `name`, `doctype`, `docstatus`, `createdAt`,\ \ `updatedAt`. A Password field comes back as a fixed redaction marker and is dropped when empty; its stored value\ \ is never returned by this or any other read on this surface.\n\n`name` in the body is the REQUESTED DOCUMENT NAME,\ \ not a data field. A DocType with an autoname rule names the document itself and ignores it; a prompt-named DocType\ \ takes it. This collision is also why the two path segments cannot be folded into the body, and so why the route\ \ stays untyped.\n\nScoped to the org of the validated principal, and the engine's own permission calculus decides\ \ the rest: the caller needs create rights on this DocType through a role it holds, or a platform admin bit. A caller\ \ with no validated principal reaches the engine as the zero Caller and is refused before any store is opened \u2014\ \ a forged org header alone buys nothing.\n\nA DocType declared Single has exactly ONE document per org, so this writes\ \ that one instance instead of adding a row. The body is size-bounded by the engine, the same bound on every host." operationId: post_framework_by_doctype parameters: - in: path name: doctype required: true schema: type: string summary: Create one document of a DocType, from that DocType's own fields. tags: - framework x-app: framework x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/framework/{doctype}/{name}: delete: description: "Removes one document, after its on_trash hooks agree. A\nSUBMITTED document cannot be deleted \u2014 cancel\ \ it first. Answers 204." operationId: delete_framework_by_doctype_by_name parameters: - description: "DocType is the document's DocType, by ADDRESS \u2014 \"module.name\", from the path." example: Projects.Task in: path name: doctype required: true schema: type: string - description: "Name is the document's name \u2014 its key within the DocType \u2014 from the path.\nA name containing\ \ a space arrives percent-encoded and is decoded before it\nis matched against the stored one." example: TASK-00001 in: path name: name required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one document, after its on_trash hooks agree. tags: - framework x-app: framework x-public: true x-tool: true get: description: Returns one document by name, with Password fields redacted. operationId: get_framework_by_doctype_by_name parameters: - description: "DocType is the document's DocType, by ADDRESS \u2014 \"module.name\", from the path." example: Projects.Task in: path name: doctype required: true schema: type: string - description: "Name is the document's name \u2014 its key within the DocType \u2014 from the path.\nA name containing\ \ a space arrives percent-encoded and is decoded before it\nis matched against the stored one." example: TASK-00001 in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: additionalProperties: {} type: object description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one document by name, with Password fields redacted. tags: - framework x-app: framework x-public: true x-tool: true put: description: "PUT semantics: the stored field data BECOMES the body, so a field the body omits is not left at its previous\ \ value. The body is the document's own field data \u2014 the same metadata-defined open object the create takes,\ \ and the same reason this operation publishes no request schema.\n\nOnly a DRAFT can be edited. A document that has\ \ been submitted or cancelled is immutable and the write is refused as a conflict, so the submit lifecycle cannot\ \ be bypassed by a plain update \u2014 cancel it first, and note that a cancelled document can be deleted but never\ \ re-submitted or re-edited. The engine validates the new data against the DocType, runs before_save (which may reject),\ \ writes, then runs the after hooks, and answers 200 with the stored document plus its managed envelope, Password\ \ fields redacted.\n\nThe document name in the path is percent-decoded before it is matched, so a name containing\ \ a space is addressed as it is stored. An unknown DocType or document is not found, and the same answer covers a\ \ document that exists in another tenant: the org comes from the validated principal and is part of the store key,\ \ so a caller cannot learn that another org's document exists. Write rights on the DocType are required, decided by\ \ the engine's permission calculus.\n\nFor a Single DocType the path name is ignored \u2014 there is one instance\ \ per org and this writes it." operationId: put_framework_by_doctype_by_name parameters: - in: path name: doctype required: true schema: type: string - in: path name: name required: true schema: type: string summary: Replace a draft document's field data wholesale. tags: - framework x-app: framework x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/framework/{doctype}/{name}/cancel: post: description: "Moves a submitted document to cancelled (docstatus 1 \u2192 2) after\nits on_cancel hooks agree. Cancelling\ \ is terminal \u2014 a cancelled document\ncannot be re-submitted \u2014 but it CAN then be deleted." operationId: post_framework_by_doctype_by_name_cancel parameters: - description: "DocType is the document's DocType, by ADDRESS \u2014 \"module.name\", from the path." example: Projects.Task in: path name: doctype required: true schema: type: string - description: "Name is the document's name \u2014 its key within the DocType \u2014 from the path.\nA name containing\ \ a space arrives percent-encoded and is decoded before it\nis matched against the stored one." example: TASK-00001 in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: additionalProperties: {} type: object description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Moves a submitted document to cancelled (docstatus 1 \u2192 2) after its on_cancel hooks agree." tags: - framework x-app: framework x-public: true x-tool: true /v1/framework/{doctype}/{name}/submit: post: description: "Moves a draft to submitted (docstatus 0 \u2192 1) after its\non_submit hooks agree. A submitted document\ \ is IMMUTABLE: further writes and\ndeletes are refused until it is cancelled. Only a submittable DocType has this\n\ lifecycle; any other docstatus is an illegal transition." operationId: post_framework_by_doctype_by_name_submit parameters: - description: "DocType is the document's DocType, by ADDRESS \u2014 \"module.name\", from the path." example: Projects.Task in: path name: doctype required: true schema: type: string - description: "Name is the document's name \u2014 its key within the DocType \u2014 from the path.\nA name containing\ \ a space arrives percent-encoded and is decoded before it\nis matched against the stored one." example: TASK-00001 in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: additionalProperties: {} type: object description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Moves a draft to submitted (docstatus 0 \u2192 1) after its on_submit hooks agree." tags: - framework x-app: framework x-public: true x-tool: true /v1/function: get: description: "Is every serverless function the caller's org has published, each with its\nreal 7-day rollup.\n\nA row\ \ carries the function's runtime, resource limits, deployment target and its\ninvoke endpoint, plus envCount \u2014\ \ how many secrets it mounts. The rollup fields\nare ABSENT rather than zero when the function has not run in the\ \ window, so a\nconsole renders \"\u2014\" instead of a fabricated 0.\n\nRequires a validated principal; the listing\ \ is scoped to its org." operationId: get_function responses: '200': content: application/json: schema: $ref: '#/components/schemas/function.fnList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Is every serverless function the caller's org has published, each with its real 7-day rollup. tags: - function x-app: function x-public: true x-tool: true post: description: "Publishes a serverless function under the caller's org and answers 201\nwith it.\n\nThe name is the key\ \ and is claimed once; the names that would shadow a\ncollection route are reserved. runtime and environment are the\ \ same field \u2014\neither spelling is accepted \u2014 and default to node.\n\nBounds are clamped rather than refused\ \ where a clamp is honest: a timeout above\nthe 900-second ceiling becomes the ceiling instead of silently reverting\ \ to the\n30-second default, and an omitted memory limit becomes 256Mi. target=fleet runs\non the org's own GPU fleet\ \ and supports runtime=python only.\n\nRequires a validated principal; the function is owned by that principal's org." operationId: post_function requestBody: content: application/json: schema: $ref: '#/components/schemas/function.definition' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/function.functionView' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Publishes a serverless function under the caller's org and answers 201 with it. tags: - function x-app: function x-public: true x-tool: true /v1/function/deployments: get: description: "Is what is live right now \u2014 each function's current record IS its\nlive deployment, so this is the\ \ deployment inventory.\n\nThere is no deployment history behind it: a function has one record, and\npublishing replaces\ \ it. The 7-day rollup is deliberately absent here, because\nthis read is about what is deployed rather than about\ \ how it has performed." operationId: get_function_deployments responses: '200': content: application/json: schema: $ref: '#/components/schemas/function.fnList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Is what is live right now \u2014 each function's current record IS its live deployment, so this is the deployment\ \ inventory." tags: - function x-app: function x-public: true x-tool: true /v1/function/metrics: get: description: "Is the org's serverless dashboard over a window: a per-function\ninvocation costLine and how those invocations\ \ ended.\n\nEvery point is a REAL count of rows that fell in that bucket \u2014 nothing is\ninterpolated or invented,\ \ so an empty window draws a flat line rather than a\nfabricated one.\n\ncostCents is null and stays null: there is\ \ no per-invocation cost source to read,\nand reporting a number computed some other way would be a guess presented\ \ as a\nmeasurement. Requires a validated principal; the read is scoped to its org." operationId: get_function_metrics parameters: - description: 'Range is 1H, 6H, 24H (the default), 7D or 30D. Anything else falls back to 24H rather than failing.' in: query name: range required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/function.usage' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Is the org''s serverless dashboard over a window: a per-function invocation costLine and how those invocations ended.' tags: - function x-app: function x-public: true x-tool: true /v1/function/secrets: get: description: "Is the NAMES of the secrets the caller org's functions mount.\n\nValues are NEVER read or returned \u2014\ \ this surface knows which names a function\nasks for and nothing about what is behind them, which is what makes it\ \ safe to\nlist at all. One row per distinct (namespace, name)." operationId: get_function_secrets responses: '200': content: application/json: schema: $ref: '#/components/schemas/function.secretList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Is the NAMES of the secrets the caller org's functions mount. tags: - function x-app: function x-public: true x-tool: true /v1/function/triggers: get: description: "Is what calls the caller org's functions \u2014 one row per function.\n\nEvery function has exactly one\ \ trigger today, its HTTP invoke endpoint, so this\nis the function list read as \"how is each of these reached\"." operationId: get_function_triggers responses: '200': content: application/json: schema: $ref: '#/components/schemas/function.triggerList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Is what calls the caller org's functions \u2014 one row per function." tags: - function x-app: function x-public: true x-tool: true /v1/function/{name}: delete: description: "Removes one of the caller org's functions and answers 204.\n\nA name this org does not hold is 404 \u2014\ \ never a silent success \u2014 and a name\nbelonging to another tenant is the same 404, because the delete is predicated\ \ on\nthe validated org." operationId: delete_function_by_name parameters: - description: Name is the function the URL names. in: path name: name required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one of the caller org's functions and answers 204. tags: - function x-app: function x-public: true x-tool: true get: description: 'Is one function with everything a detail page needs in one round-trip: its definition, its 7-day rollup, its trigger, its twenty most recent invocations and the NAMES of the secrets it mounts. Secret values are never read or returned. A name the caller''s org does not hold is 404, which is also what another tenant''s function looks like from here.' operationId: get_function_by_name parameters: - description: Name is the function the URL names. in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/function.functionDetail' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Is one function with everything a detail page needs in one round-trip: its definition, its 7-day rollup, its trigger, its twenty most recent invocations and the NAMES of the secrets it mounts.' tags: - function x-app: function x-public: true x-tool: true /v1/function/{name}/invocations: get: description: "Is one function's past runs, newest first \u2014 each with its status,\nHTTP code, method, time and duration.\n\ \nThese are real recorded rows, not a projection: an invocation appears here only\nonce it actually ran. Requires\ \ a validated principal; the read is scoped to its\norg." operationId: get_function_by_name_invocations parameters: - description: Name is the function the URL names. in: path name: name required: true schema: type: string - description: Limit caps the page, defaulting to 100. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/function.invocationList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Is one function's past runs, newest first \u2014 each with its status, HTTP code, method, time and duration." tags: - function x-app: function x-public: true x-tool: true /v1/function/{name}/invoke: post: description: "Runs a function and records a REAL invocation.\n\nThe answer is the invocation record whatever happened\ \ to it: 200 when the org's\ncode ran clean, 502 when it ran and failed, 503 when this deployment has no\nsandbox\ \ to run code in. The record IS the evidence, so it rides the failure\nrather than being replaced by an error envelope.\n\ \nBilling is two-part and both parts are prepaid-then-metered on the one shared\nmeter: a flat per-invocation request\ \ fee, gated BEFORE any sandbox compute runs\nso an unfunded org gets 402 and nothing executes, and a usage-native\n\ GB-seconds compute debit taken after the run. Either is independently free when\nits fee is zero, so an operator can\ \ bill by request alone, by compute alone, or\nby both \u2014 and a zero request fee removes the balance gate with\ \ it.\n\nA TRANSPORT failure is not charged: the sandbox being unreachable ran no\nbillable compute. Code that ran\ \ and exited non-zero IS charged \u2014 that is a\nsuccessful invocation of a failing program, not a billing failure.\n\ \nWhen the sandbox is not configured on this deployment, a non-fleet function\nfails closed before anything is recorded\ \ \u2014 no execution and no fabricated\noutput. Scoped to the caller's org; requires a validated principal." operationId: post_function_by_name_invoke parameters: - in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/function.invokeReq' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/function.invocationView' description: ok '502': content: application/json: schema: $ref: '#/components/schemas/function.invocationView' description: bad gateway '503': content: application/json: schema: $ref: '#/components/schemas/function.invocationView' description: service unavailable default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Runs a function and records a REAL invocation. tags: - function x-app: function x-public: true x-tool: true /v1/function/{name}/logs: get: description: "Is the output of a function's most recent run \u2014 its error text when that\nrun failed, else what it\ \ printed.\n\nIt is the LAST run only, and it is empty when the function has never run. There\nis no log retention\ \ behind this beyond the recorded invocation itself." operationId: get_function_by_name_logs parameters: - description: Name is the function the URL names. in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/function.logLines' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Is the output of a function's most recent run \u2014 its error text when that run failed, else what it printed." tags: - function x-app: function x-public: true x-tool: true /v1/gateway/config: get: description: 'Returns the EFFECTIVE edge policy the caller is subject to: the platform CORS allowlist and pre-auth per-IP flood cap in force, plus the caller''s own authenticated rate ceiling, edge-cache TTLs and accepted-method allowlist. A SuperAdmin may inspect a specific tenant''s effective policy with ?org=.' operationId: get_gateway_config responses: '200': content: application/json: schema: $ref: '#/components/schemas/gateway.Policy' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the EFFECTIVE edge policy the caller is subject to: the platform CORS allowlist and pre-auth per-IP flood cap in force, plus the caller''s own authenticated rate ceiling, edge-cache TTLs and accepted-method allowlist.' tags: - gateway x-app: gateway x-public: true x-tool: true put: description: "Updates one policy scope and returns the policy in force after the write.\nA body carrying any PLATFORM\ \ field (cors_origins, per_ip_rpm, window_sec) is a\nplatform write and requires SuperAdmin; otherwise it is a per-org\ \ write (org_rpm,\ncache_ttl_sec, cache_paths, methods) scoped to the caller's own org \u2014 or, for a\nSuperAdmin,\ \ the tenant named by ?org=. A body that sets nothing is a 400.\nThe abuse gate's mode is an OPERATOR field:\ \ setting it requires SuperAdmin,\nwhichever organization it lands on. updated_at and updated_by are\nserver-stamped;\ \ a client-supplied value is ignored." operationId: put_gateway_config requestBody: content: application/json: example: cache_ttl_sec: 30 methods: - GET - POST org_rpm: 120 schema: $ref: '#/components/schemas/gateway.Policy' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/gateway.Policy' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Updates one policy scope and returns the policy in force after the write. tags: - gateway x-app: gateway x-public: true x-tool: true /v1/gateway/traffic: get: description: "Reports who is calling this organization's API right now: the request\ncount for the last minute split\ \ by AGENCY LANE \u2014 agent, human, bot, unknown \u2014\nand the busiest callers behind it, each with its request\ \ count, its\nauthentication-failure count, how many distinct paths it touched, and any\nverdict currently held against\ \ it.\n\nThe lane split is the answer to the question a generic bot filter cannot\nanswer: which of this traffic is\ \ the customer's own automation and which is\nsomebody working through a list. It is computed from credentials we\ \ issued, not\nfrom the client's self-description, so a scraper cannot move itself into the\nagent lane by editing\ \ a header.\n\nA validated caller appears as a FINGERPRINT \u2014 a one-way, per-process digest. It\nis stable enough\ \ to recognise the same caller across a minute and cannot be\nturned back into a key, so this report is safe to read,\ \ screenshot and paste.\n\nIt also reports what the sensor's own ceilings are doing (strain, tracked,\nceiling, refused)\ \ and how many screens the scorer did not answer (unscored), so\na control that has stopped measuring or a judge that\ \ has stopped answering is a\nnumber here rather than a quiet day.\n\nScoped to the caller's own validated organization.\ \ A SuperAdmin may inspect a\nspecific tenant with ?org=, or the lane that has no tenant \u2014 every caller\n\ the identity boundary could not validate \u2014 with an empty ?org=." operationId: gatewayTraffic responses: '200': content: application/json: schema: $ref: '#/components/schemas/gateway.TrafficView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Report who is calling this org's API right now tags: - gateway x-app: gateway x-public: true x-tool: true /v1/git: get: description: "The repository list for the signed-in caller's org \u2014 each repo with its description, default branch,\ \ size and last update. SIGNED OUT it renders the public explore page instead of refusing, because most Hanzo repos\ \ are open source and the open face is the default one; signed in, the caller's own org shows its private repositories\ \ alongside its public ones. This is a server-rendered browser page, not JSON \u2014 the console repo-browser reads\ \ the same repository through the JSON ops under /v1/git/repos. Repository names, paths and file contents all render\ \ through auto-escaping templates rather than being concatenated into HTML." operationId: get_git summary: Browse your org's repositories tags: - git x-app: git x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/git/explore: get: description: "The open, unauthenticated face of the git host: every PUBLIC repository in the fleet, org-qualified, so\ \ a project can be found and cloned with no account at all \u2014 signing in is for private repos and for writes.\ \ Repositories live in per-org stores with no global index, so this unions each org's public rows and is bounded to\ \ a fixed number of stores per request, keeping discovery quick however many orgs exist. A fleet with no orgs yet\ \ is an empty page, not an error. This is a server-rendered browser page, not JSON \u2014 the console repo-browser\ \ reads the same repository through the JSON ops under /v1/git/repos. Repository names, paths and file contents all\ \ render through auto-escaping templates rather than being concatenated into HTML." operationId: get_git_explore summary: Discover public repositories across every org tags: - git x-app: git x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/git/keys: get: description: "Returns the SSH public keys registered to the caller's org \u2014 the keys\nthat authenticate `git clone\ \ git@:/.git`. Keys are org-scoped\non read even though the fingerprint index is global, so one org\ \ never sees\nanother's." operationId: get_git_keys responses: '200': content: application/json: example: data: - createdAt: '2026-07-01T10:00:00Z' fingerprint: "SHA256:9pQ\u2026" id: gitkey_4a1b publicKey: "ssh-ed25519 AAAAC3Nz\u2026" title: laptop schema: $ref: '#/components/schemas/git.keyList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the SSH public keys registered to the caller's org \u2014 the keys that authenticate `git clone git@:/.git`." tags: - git x-app: git x-public: true x-tool: true post: description: "Registers an SSH public key so it can authenticate `git clone\ngit@:/.git` for the caller's\ \ org. The key line is parsed and\ncanonicalized before storage, its SHA256 fingerprint becomes the auth lookup\n\ handle, and the full public key round-trips (it is public). Answers 201.\nFingerprints are globally unique, so a key\ \ already registered \u2014 to this org or\nany other \u2014 is a 409: one key belongs to exactly one org." operationId: post_git_keys requestBody: content: application/json: example: publicKey: "ssh-ed25519 AAAAC3Nz\u2026 z@hanzo.ai" title: laptop schema: $ref: '#/components/schemas/git.registerKeyReq' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/git.keyView' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Registers an SSH public key so it can authenticate `git clone git@:/.git` for the caller's org. tags: - git x-app: git x-public: true x-tool: true /v1/git/keys/{id}: delete: description: 'Removes a registered SSH key, scoped to the caller''s org: an org can only delete its own, and a key id it does not own is not found. Answers 204 with no body. Once removed the key no longer authenticates any SSH git access.' operationId: delete_git_keys_by_id parameters: - description: "ID is the key's identifier (\"gitkey_\u2026\"), from the :id path segment." example: gitkey_4a1b in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Removes a registered SSH key, scoped to the caller''s org: an org can only delete its own, and a key id it does not own is not found.' tags: - git x-app: git x-public: true x-tool: true /v1/git/pools: get: description: 'Returns the capacity this org has declared and how many daemons have entered each pool.' operationId: get_git_pools responses: '200': content: application/json: schema: $ref: '#/components/schemas/git.poolList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the capacity this org has declared and how many daemons have entered each pool. tags: - git x-app: git x-public: true x-tool: true post: description: 'Records the capacity an org has, and answers with the secret a runner daemon presents to enter it. Declaring is the ONLY way capacity comes to exist: a daemon cannot register against a pool nobody declared, because the secret it would have to present does not exist until this runs. Re-declaring an existing pool replaces its labels and mints a fresh secret; runners already inside it keep working.' operationId: post_git_pools requestBody: content: application/json: example: labels: - ubuntu-latest - linux/amd64 name: evo schema: $ref: '#/components/schemas/git.poolDeclare' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/git.poolDeclared' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records the capacity an org has, and answers with the secret a runner daemon presents to enter it. tags: - git x-app: git x-public: true x-tool: true /v1/git/repos: get: description: "Returns the repos in the caller's scope, most recently updated\nfirst. The scope is the request principal's\ \ \u2014 the gateway-minted org and its\noptional project \u2014 never anything off the wire, so a caller only ever\ \ sees its\nown. Rows carry no branches or HEAD; read one repo for those." operationId: get_git_repos responses: '200': content: application/json: example: data: - cloneUrl: https://api.hanzo.ai/v1/git/acme/widgets.git createdAt: '2026-07-01T10:00:00Z' defaultBranch: main id: repo_9f3c name: widgets org: acme public: false sizeBytes: 4096 sshUrl: git@git.hanzo.ai:acme/widgets.git schema: $ref: '#/components/schemas/git.repoList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the repos in the caller's scope, most recently updated first. tags: - git x-app: git x-public: true x-tool: true post: description: "Provisions an empty bare repository in the caller's scope and\nreturns it with its clone URLs. Answers\ \ 201. The name must be unique within\nthe scope \u2014 a repeat is a 409, never a silent overwrite of an existing\ \ repo.\nThe org comes from the validated principal, so a repo is always born owned by\nthe caller's own tenant." operationId: post_git_repos requestBody: content: application/json: example: description: the widget service name: widgets schema: $ref: '#/components/schemas/git.createReq' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/git.repoView' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Provisions an empty bare repository in the caller's scope and returns it with its clone URLs. tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}: delete: description: "Removes a repo's metadata and purges its storage. Answers 204 with\nno body. The metadata row is the source\ \ of truth for existence, so a storage\npurge that fails is logged and the delete still succeeds \u2014 and a second\ \ call\nis a 404, not a second delete." operationId: delete_git_repos_by_name parameters: - description: 'Name is the repo''s org-unique handle, from the :name path segment. A trailing ".git" is stripped.' example: widgets in: path name: name required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a repo's metadata and purges its storage. tags: - git x-app: git x-public: true x-tool: true get: description: 'Returns one repo with its live ref state: every branch name and the resolved HEAD commit. Both are read from the object store on each call, so an empty repo reports no branches and an empty head rather than failing. A repo outside the caller''s scope is not found.' operationId: get_git_repos_by_name parameters: - description: 'Name is the repo''s org-unique handle, from the :name path segment. A trailing ".git" is stripped.' example: widgets in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/git.repoView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one repo with its live ref state: every branch name and the resolved HEAD commit.' tags: - git x-app: git x-public: true x-tool: true patch: description: 'Flips a repo''s public bit, the one mutable repo setting today. Public grants ANONYMOUS fetch only; push and the whole control plane stay org-authed. Returns the updated repo.' operationId: patch_git_repos_by_name parameters: - description: Name is the repo to update, from the :name path segment. example: widgets in: path name: name required: true schema: type: string requestBody: content: application/json: example: name: widgets public: true schema: $ref: '#/components/schemas/git.patchIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/git.repoView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Flips a repo's public bit, the one mutable repo setting today. tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}/blob: get: description: "Returns one file's bytes at one revision. Text comes back verbatim,\nbinary comes back base64, and a file\ \ past the 1 MiB view cap comes back marked\ntruncated with NO content \u2014 the client is expected to clone instead." operationId: get_git_repos_by_name_blob parameters: - description: Name is the repo to read, from the :name path segment. example: widgets in: path name: name required: true schema: type: string - description: Ref is a branch, tag or commit; empty means the repo's HEAD. example: main in: query name: ref required: false schema: type: string - description: Path is repo-relative; empty is the tree root. Traversal is stripped. example: go.mod in: query name: path required: false schema: type: string responses: '200': content: application/json: example: binary: false content: 'module widgets ' encoding: utf8 path: go.mod size: 42 truncated: false schema: $ref: '#/components/schemas/git.blobJSON' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one file's bytes at one revision. tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}/commits: get: description: 'Walks a ref''s history newest first, or one path''s history when a path is given. There is no cursor: the page is the newest `limit` commits.' operationId: get_git_repos_by_name_commits parameters: - description: Name is the repo to read, from the :name path segment. example: widgets in: path name: name required: true schema: type: string - description: Ref is the branch, tag or commit to walk back from; empty means HEAD. example: main in: query name: ref required: false schema: type: string - description: Path narrows the history to commits touching it; empty walks the whole ref. in: query name: path required: false schema: type: string - description: Limit caps the page. Anything not positive means 50; the cap is 100. example: 2 in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: example: commits: - authorEmail: ada@hanzo.ai authorName: Ada date: '2026-07-01T10:00:00Z' message: add the widget service sha: a1b2c3d4e5f6 shortSha: a1b2c3d schema: $ref: '#/components/schemas/git.commitsJSON' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Walks a ref's history newest first, or one path's history when a path is given. tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}/files: get: description: "Returns every file a glob selects at one revision, WITH its bytes\nand the revision they came from. It\ \ is the read a delivery generator makes:\none call answers \"what is the inventory at this commit, and what does\ \ it say\",\nwhere listing and then fetching would be a request per file.\n\nReturning the resolved revision matters\ \ as much as the bytes. A generator that\nlists at `main` and then reads at `main` can straddle a push and assemble\ \ half\nits inventory from one commit and half from the next; resolving once makes the\nwhole read consistent by construction.\n\ \nA file past the read cap comes back Truncated with no content rather than\nbeing dropped. A caller building a desired\ \ set has to know the difference\nbetween \"this file is empty\" and \"this file was not read\" \u2014 silently omitting\n\ it is how a pruning reconcile deletes what the missing file declared." operationId: get_git_repos_by_name_files parameters: - description: Name is the repo to read, from the :name path segment. example: universe in: path name: name required: true schema: type: string - description: Ref is a branch, tag or commit; empty means the repo's HEAD. example: main in: query name: ref required: false schema: type: string - description: 'Glob selects files, matched segment by segment so `*` never crosses a `/`. `**` matches zero or more whole segments.' example: charts/app/values/*/*.yaml in: query name: glob required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/git.filesJSON' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns every file a glob selects at one revision, WITH its bytes and the revision they came from. tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}/gc: post: description: "Repacks a repo into one bitmapped pack and rewrites its commit-graph, so\nthe next clone reuses the bitmap\ \ instead of walking the whole object graph.\nIdempotent, and safe to interrupt \u2014 git swaps both artifacts atomically.\ \ It\nruns under one pack slot with the same memory bounds as a clone, so it can\nblock behind heavy pack traffic\ \ rather than compete with it. Storage usage is\nre-measured afterwards, since a repack reclaims space." operationId: post_git_repos_by_name_gc parameters: - description: 'Name is the repo''s org-unique handle, from the :name path segment. A trailing ".git" is stripped.' example: widgets in: path name: name required: true schema: type: string responses: '200': content: application/json: example: maintained: true repo: widgets sizeBytes: 3072 schema: $ref: '#/components/schemas/git.gcOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Repacks a repo into one bitmapped pack and rewrites its commit-graph, so the next clone reuses the bitmap instead of walking the whole object graph. tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}/mirror: post: description: "Imports an external git repository into the caller's repo, provisioning\nit on first use. Fetch is FORCED\ \ and covers every ref, so a first call clones\nthe source and a repeat call re-syncs it \u2014 the endpoint is idempotent\ \ by mirror\nsemantics. Mirrored bytes are metered exactly like a push, and a push.landed\nevent is emitted for the\ \ default branch so the code index picks the repo up." operationId: post_git_repos_by_name_mirror parameters: - description: 'Name is the local repo to mirror into, from the :name path segment. It is CREATED on first use.' example: widgets in: path name: name required: true schema: type: string requestBody: content: application/json: example: name: widgets source: https://github.com/acme/widgets.git schema: $ref: '#/components/schemas/git.mirrorReq' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/git.repoView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Imports an external git repository into the caller's repo, provisioning it on first use. tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}/pulls: get: description: "Returns a repo's pull requests, newest number first \u2014 what is\nwaiting to be reviewed, and what has\ \ already landed. Narrow it with\n?state=open or ?state=merged; omit state for every proposal." operationId: get_git_repos_by_name_pulls parameters: - description: Name is the repo, from the :name path segment. example: widgets in: path name: name required: true schema: type: string - description: State narrows the list to "open" or "merged". Omit it for every proposal. example: open in: query name: state required: false schema: type: string responses: '200': content: application/json: example: data: - base: main createdAt: '2026-08-07T10:00:00Z' head: agent/cache-catalog number: 4 repo: widgets state: open title: cache the catalog read updatedAt: '2026-08-07T10:00:00Z' schema: $ref: '#/components/schemas/git.pullList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns a repo's pull requests, newest number first \u2014 what is waiting to be reviewed, and what has already\ \ landed." tags: - git x-app: git x-public: true x-tool: true post: description: "Proposes a branch for merging and returns it with its number. Answers\n201. Both branches must already\ \ exist \u2014 a proposal naming a branch nobody\npushed is a typo, not a plan \u2014 and base defaults to the repo's\ \ default branch.\n\nProposing the same head into the same base twice is a 409 while the first\nproposal is still\ \ open, so a retried agent run leaves ONE thing to review\nrather than a pile of identical ones. A repo outside the\ \ caller's scope is a\n404, exactly as reading it is." operationId: post_git_repos_by_name_pulls parameters: - description: Name is the repo the proposal belongs to, from the :name path segment. example: widgets in: path name: name required: true schema: type: string requestBody: content: application/json: example: base: main head: agent/cache-catalog name: widgets title: cache the catalog read schema: $ref: '#/components/schemas/git.openReq' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/git.pullView' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Proposes a branch for merging and returns it with its number. tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}/pulls/{number}: get: description: 'Returns one pull request by its per-repo number. A number belonging to another tenant''s repo is not found, exactly as the repo itself is not.' operationId: get_git_repos_by_name_pulls_by_number parameters: - description: Name is the repo, from the :name path segment. example: widgets in: path name: name required: true schema: type: string - description: Number is the proposal's per-repo number, from the :number path segment. example: 4 in: path name: number required: true schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/git.pullView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one pull request by its per-repo number. tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}/pulls/{number}/merge: post: description: "Merges an open pull request by FAST-FORWARDING base to head, and\nanswers the proposal in its merged state\ \ with the revision base now points at.\n\nIt merges only when base is already an ancestor of head \u2014 the case\ \ where head\ncontains every commit base has, so moving the branch loses nothing and invents\nnothing. When base has\ \ moved on independently, this REFUSES with 409 and says\nso: a real three-way merge is not implemented here, and\ \ reporting one would\nclaim a result these bytes do not produce. Rebase head onto base and merge\nagain.\n\nThe move\ \ is judged by the same ref policy a `git push` of it would face, and\nfires the same build and notify reactions,\ \ so merging is not a way around\neither. Merging an already-merged proposal is a 409." operationId: post_git_repos_by_name_pulls_by_number_merge parameters: - description: Name is the repo, from the :name path segment. example: widgets in: path name: name required: true schema: type: string - description: Number is the proposal's per-repo number, from the :number path segment. example: 4 in: path name: number required: true schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/git.pullView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Merges an open pull request by FAST-FORWARDING base to head, and answers the proposal in its merged state with the revision base now points at. tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}/push: post: description: "Lands a set of files as one commit without a git client \u2014 the\nhanzo.app builder's push. The repo\ \ is CREATED on first push, the files are\nmerged onto the branch tip (unlisted files survive), and the same\npush-to-deploy\ \ hook a real receive-pack fires is fired, so downstream this is\nindistinguishable from a `git push`.\n\nIt is for\ \ generated content. Changing CI through it needs an org admin (403\notherwise), and a workflow whose newest change\ \ came in through it is not run\nautomatically; CI written through git runs as usual." operationId: post_git_repos_by_name_push parameters: - description: 'Name is the repo to push into, from the :name path segment. It is CREATED on first push if it does not exist.' example: widgets in: path name: name required: true schema: type: string requestBody: content: application/json: example: branch: main files: - content:

hi

path: index.html message: generated build name: widgets schema: $ref: '#/components/schemas/git.pushReq' required: true responses: '200': content: application/json: example: branch: main cloneUrl: https://api.hanzo.ai/v1/git/acme/widgets.git commit: a1b2c3d4e5f6 sshUrl: git@git.hanzo.ai:acme/widgets.git schema: $ref: '#/components/schemas/git.pushResp' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lands a set of files as one commit without a git client \u2014 the hanzo.app builder's push." tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}/readme: get: description: "Returns the README at the tree root as plain text \u2014 unrendered, so\nthe caller decides how to present\ \ it. A repo with no README is not found." operationId: get_git_repos_by_name_readme parameters: - description: Name is the repo to read, from the :name path segment. example: widgets in: path name: name required: true schema: type: string - description: Ref is a branch, tag or commit; empty means the repo's HEAD. example: main in: query name: ref required: false schema: type: string responses: '200': content: application/json: example: content: '# widgets ' encoding: utf8 path: README.md schema: $ref: '#/components/schemas/git.readmeJSON' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the README at the tree root as plain text \u2014 unrendered, so the caller decides how to present\ \ it." tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}/refs: get: description: "Lists a repo's branches, tags and default branch \u2014 what a branch\npicker needs in one call. Unlike\ \ the other read ops it tolerates a repo with no\ncommits: the ref sets come back empty and the default branch is\ \ still named." operationId: get_git_repos_by_name_refs parameters: - description: 'Name is the repo''s org-unique handle, from the :name path segment. A trailing ".git" is stripped.' example: widgets in: path name: name required: true schema: type: string responses: '200': content: application/json: example: branches: - name: main sha: a1b2c3d4 default: main tags: [] schema: $ref: '#/components/schemas/git.refsJSON' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists a repo's branches, tags and default branch \u2014 what a branch picker needs in one call." tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}/subscriptions: get: description: "Returns a repo's Slack subscriptions \u2014 which channels the\nlifecycle notifier posts this repo's push\ \ and deploy events to." operationId: get_git_repos_by_name_subscriptions parameters: - description: 'Name is the repo''s org-unique handle, from the :name path segment. A trailing ".git" is stripped.' example: widgets in: path name: name required: true schema: type: string responses: '200': content: application/json: example: data: - channel: '#builds' createdAt: '2026-07-01T10:00:00Z' events: - push.landed id: sub_7c2e repo: widgets schema: $ref: '#/components/schemas/git.subscriptionList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns a repo's Slack subscriptions \u2014 which channels the lifecycle notifier posts this repo's push and\ \ deploy events to." tags: - git x-app: git x-public: true x-tool: true post: description: 'Binds a Slack channel to a repo, so the lifecycle notifier posts that repo''s push and deploy events there. Answers 201. The same channel twice on one repo is a 409; a repo outside the caller''s scope is a 404, exactly as reading it is.' operationId: post_git_repos_by_name_subscriptions parameters: - description: Name is the repo to subscribe, from the :name path segment. example: widgets in: path name: name required: true schema: type: string requestBody: content: application/json: example: channel: '#builds' events: - push.landed name: widgets schema: $ref: '#/components/schemas/git.subscribeReq' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/git.subscriptionView' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Binds a Slack channel to a repo, so the lifecycle notifier posts that repo's push and deploy events there. tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}/subscriptions/{id}: delete: description: 'Removes one Slack subscription from a repo; the notifier stops posting that repo''s events to that channel. Answers 204 with no body. An id that is not this repo''s subscription is not found.' operationId: delete_git_repos_by_name_subscriptions_by_id parameters: - description: Name is the repo, from the :name path segment. example: widgets in: path name: name required: true schema: type: string - description: ID is the row to remove, from the :id path segment. example: sub_7c2e in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one Slack subscription from a repo; the notifier stops posting that repo's events to that channel. tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}/targets: get: description: "Returns a repo's outbound mirror targets \u2014 the downstream remotes\nthe mirror reactor pushes to whenever\ \ a push lands here." operationId: get_git_repos_by_name_targets parameters: - description: 'Name is the repo''s org-unique handle, from the :name path segment. A trailing ".git" is stripped.' example: widgets in: path name: name required: true schema: type: string responses: '200': content: application/json: example: data: - createdAt: '2026-07-01T10:00:00Z' host: github.com id: mir_2d90 repo: widgets url: https://github.com/acme/widgets.git schema: $ref: '#/components/schemas/git.mirrorList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns a repo's outbound mirror targets \u2014 the downstream remotes the mirror reactor pushes to whenever\ \ a push lands here." tags: - git x-app: git x-public: true x-tool: true post: description: "Registers a downstream remote the repo's advanced refs are pushed to\nwhenever a push lands here. Answers\ \ 201. The URL must be https to a host on the\nmirror allowlist (github.com / gitlab.com): the same set the mirror\ \ credential\nmay be sent to, so a target can never capture the shared token or point the push\nat an internal service.\ \ Any embedded userinfo is stripped \u2014 credentials ride\nenv-only at push time and never enter the stored URL.\ \ One mirror per host per\nrepo; a second is a 409." operationId: post_git_repos_by_name_targets parameters: - description: 'Name is the repo whose advanced refs are pushed downstream, from the :name path segment.' example: widgets in: path name: name required: true schema: type: string requestBody: content: application/json: example: name: widgets url: https://github.com/acme/widgets.git schema: $ref: '#/components/schemas/git.mirrorTargetReq' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/git.mirrorTargetView' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Registers a downstream remote the repo's advanced refs are pushed to whenever a push lands here. tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}/targets/{id}: delete: description: "Removes one outbound mirror target; later pushes stop being\nforwarded to it. Answers 204 with no body.\ \ Nothing is done to the downstream\nremote itself \u2014 only this repo's intent to push there is dropped." operationId: delete_git_repos_by_name_targets_by_id parameters: - description: Name is the repo, from the :name path segment. example: widgets in: path name: name required: true schema: type: string - description: ID is the row to remove, from the :id path segment. example: mir_2d90 in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one outbound mirror target; later pushes stop being forwarded to it. tags: - git x-app: git x-public: true x-tool: true /v1/git/repos/{name}/tree: get: description: "Lists the immediate children of one directory at one revision,\ndirectories before files. It does not\ \ recurse \u2014 walk down a level at a time." operationId: get_git_repos_by_name_tree parameters: - description: Name is the repo to read, from the :name path segment. example: widgets in: path name: name required: true schema: type: string - description: Ref is a branch, tag or commit; empty means the repo's HEAD. example: main in: query name: ref required: false schema: type: string - description: Path is repo-relative; empty is the tree root. Traversal is stripped. example: cmd in: query name: path required: false schema: type: string responses: '200': content: application/json: example: entries: - mode: '040000' name: server path: cmd/server size: 0 type: tree schema: $ref: '#/components/schemas/git.treeJSON' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the immediate children of one directory at one revision, directories before files. tags: - git x-app: git x-public: true x-tool: true /v1/git/runners: get: description: 'Returns the daemons registered into this org''s pools, newest first, with when each was last heard from.' operationId: get_git_runners responses: '200': content: application/json: schema: $ref: '#/components/schemas/git.runnerList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the daemons registered into this org's pools, newest first, with when each was last heard from. tags: - git x-app: git x-public: true x-tool: true /v1/git/runs: get: description: Returns this org's runs, newest first. operationId: get_git_runs parameters: - description: Repo restricts the listing to one repository. Empty lists the whole org. in: query name: repo required: false schema: type: string - description: Limit caps the answer; 0 means the default of 50, and 200 is the ceiling. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/git.workflowRuns' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns this org's runs, newest first. tags: - git x-app: git x-public: true x-tool: true post: description: 'Runs a repository''s workflows at a ref, on demand. It takes the SAME path a push takes: the request is recorded in the journal and delivered from there, so an explicit run and a pushed one are one mechanism with one idempotency rule and not two that can disagree. Asking twice for the same commit yields the same run.' operationId: post_git_runs requestBody: content: application/json: example: ref: main repo: widgets schema: $ref: '#/components/schemas/git.runStart' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/git.workflowRuns' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Runs a repository's workflows at a ref, on demand. tags: - git x-app: git x-public: true x-tool: true /v1/git/runs/{id}: get: description: Returns one run. operationId: get_git_runs_by_id parameters: - description: ID is the run to read, from the :id path segment. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/git.workflowRun' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one run. tags: - git x-app: git x-public: true x-tool: true /v1/git/usage: get: description: "Returns per-repo and total storage bytes for the caller's org \u2014 the\nqueryable, per-tenant number\ \ commerce and o11y meter on. It spans EVERY\nproject sub-scope, unlike the repo list, so a billing consumer sees\ \ the whole\ntenant footprint in one call. Sizes are last-measured values (create, push,\nmirror and gc each re-measure),\ \ not a live walk of the disk." operationId: get_git_usage responses: '200': content: application/json: example: org: acme repos: - name: widgets sizeBytes: 4096 - name: site project: web sizeBytes: 8192 totalBytes: 12288 schema: $ref: '#/components/schemas/git.usageView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns per-repo and total storage bytes for the caller's org \u2014 the queryable, per-tenant number commerce\ \ and o11y meter on." tags: - git x-app: git x-public: true x-tool: true /v1/git/webhook: post: description: "GONE (410). Push-to-deploy is not triggered by an inbound webhook. A push into this host's own git server\ \ fires the builder in-process, and a repository whose canonical home is GitHub is delivered by the Hanzo Platform\ \ GitHub App to POST /v1/provider/github/webhook. The forge does not report a push over HTTP.\n\nEvery delivery answers\ \ 410 whatever it carries \u2014 this endpoint reads no body and authenticates nothing.\n\n410 rather than 404, because\ \ the address was real and its meaning moved, which is the distinction 410 carries. A 404 from this estate is ambiguous:\ \ Hanzo Git serves /v1, so /api/v1 404s too and reads as \"the API is switched off\". A retired endpoint says it is\ \ retired and names its replacement, so the answer carries its own fix." operationId: post_git_webhook summary: "Retired \u2014 push-to-deploy has no inbound webhook" tags: - git x-app: git x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/git/workflows: get: description: "Reports the workflows a repository declares at a ref and which\ndeclared pool would execute each job \u2014\ \ the answer to \"would a push here run,\nand where\"." operationId: get_git_workflows parameters: - description: Repo is the repository whose workflows to read. in: query name: repo required: false schema: type: string - description: Ref is the branch to read them at; empty means the default. in: query name: ref required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/git.workflowList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Reports the workflows a repository declares at a ref and which declared pool would execute each job \u2014\ \ the answer to \"would a push here run, and where\"." tags: - git x-app: git x-public: true x-tool: true /v1/git/{org}/{project}/{repo}/git-receive-pack: post: description: "The pack-transfer phase of a push, and the point at which a push becomes an EVENT. NEVER ANONYMOUS: a\ \ push always requires an authenticated org, and the org in the path must equal it.\n\nOnce the pack is on disk the\ \ repository's storage usage is metered and a build is fired for every branch whose tip actually moved, computed from\ \ the before/after branch diff rather than from what the client claimed. That runs on a cancel-immune context, so\ \ a client that hangs up the moment its push lands still gets its build, and it runs even when git itself exited non-zero\ \ \u2014 the refs on disk are the ground truth. Repacking housekeeping is detached and never blocks the response.\n\ \nA Content-Type other than `application/x-git-receive-pack-request` is 400. Addressed under the API prefix, with\ \ the PROJECT as a middle path segment: project scope otherwise rides a header a git client cannot send, so this path\ \ is the only usable remote for a project-scoped repository. This is git's own wire protocol, not an API call to make\ \ by hand: point a git client at the clone URL and it makes this request itself." operationId: post_git_by_org_by_project_by_repo_git-receive-pack parameters: - in: path name: org required: true schema: type: string - in: path name: project required: true schema: type: string - in: path name: repo required: true schema: type: string requestBody: content: application/octet-stream: schema: format: binary type: string summary: Accept a push, and turn it into a build tags: - git x-app: git x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/git/{org}/{project}/{repo}/git-upload-pack: post: description: "The pack-transfer phase of a clone or fetch: the request and the response are git's binary pack protocol,\ \ streamed straight through git itself \u2014 request body to git's stdin, git's stdout to the response \u2014 so\ \ a multi-gigabyte clone never lands in this process's memory.\n\nA PUBLIC repository is fetched anonymously; a private\ \ one requires its own org, and a wrong or absent org is 404 rather than a hint that the repository exists. A Content-Type\ \ other than `application/x-git-upload-pack-request` is 400. Addressed under the API prefix, with the PROJECT as a\ \ middle path segment: project scope otherwise rides a header a git client cannot send, so this path is the only usable\ \ remote for a project-scoped repository. This is git's own wire protocol, not an API call to make by hand: point\ \ a git client at the clone URL and it makes this request itself." operationId: post_git_by_org_by_project_by_repo_git-upload-pack parameters: - in: path name: org required: true schema: type: string - in: path name: project required: true schema: type: string - in: path name: repo required: true schema: type: string requestBody: content: application/octet-stream: schema: format: binary type: string summary: Serve a clone or fetch tags: - git x-app: git x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/git/{org}/{project}/{repo}/info/refs: get: description: "The ref-advertisement phase of git's smart-HTTP protocol \u2014 the first request a clone, a fetch and\ \ a push all make. `?service=` selects which: `git-upload-pack` advertises for a fetch, `git-receive-pack` for a push,\ \ and any other value is 400.\n\nANONYMOUS ONLY FOR FETCH, AND ONLY ON A PUBLIC REPOSITORY. The push advertisement\ \ always requires an authenticated org, and where a path org is present it must equal the authenticated one. A private\ \ repository reached without its org is 404, indistinguishable from one that does not exist. Addressed under the API\ \ prefix, with the PROJECT as a middle path segment: project scope otherwise rides a header a git client cannot send,\ \ so this path is the only usable remote for a project-scoped repository. This is git's own wire protocol, not an\ \ API call to make by hand: point a git client at the clone URL and it makes this request itself." operationId: get_git_by_org_by_project_by_repo_info_refs parameters: - in: path name: org required: true schema: type: string - in: path name: project required: true schema: type: string - in: path name: repo required: true schema: type: string summary: Advertise a repository's refs to a git client tags: - git x-app: git x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/git/{org}/{repo}: get: description: "A repository at a glance: its branches, the tree at the tip, its most recent commits, its README rendered,\ \ and the HTTPS and SSH clone URLs. `?ref=` selects a branch, tag or commit; the default branch is used when it is\ \ omitted. A repository with no commits yet renders its clone instructions rather than an error, which is what a caller\ \ who has just created one needs to see. A public repository is readable by anyone; a private one only by its own\ \ org. A repository that does not exist and one belonging to another org answer the SAME 404, so the page is never\ \ an existence oracle. This is a server-rendered browser page, not JSON \u2014 the console repo-browser reads the\ \ same repository through the JSON ops under /v1/git/repos. Repository names, paths and file contents all render through\ \ auto-escaping templates rather than being concatenated into HTML." operationId: get_git_by_org_by_repo parameters: - in: path name: org required: true schema: type: string - in: path name: repo required: true schema: type: string summary: Open a repository's home page tags: - git x-app: git x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/git/{org}/{repo}/commits: get: description: "The hundred most recent commits on one ref, each with its author, message and date. `?ref=` selects the\ \ branch, tag or commit, defaulting to the repository's default branch; an unknown one is 404. A public repository\ \ is readable by anyone; a private one only by its own org. A repository that does not exist and one belonging to\ \ another org answer the SAME 404, so the page is never an existence oracle. This is a server-rendered browser page,\ \ not JSON \u2014 the console repo-browser reads the same repository through the JSON ops under /v1/git/repos. Repository\ \ names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML." operationId: get_git_by_org_by_repo_commits parameters: - in: path name: org required: true schema: type: string - in: path name: repo required: true schema: type: string summary: Read a repository's commit log tags: - git x-app: git x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/git/{org}/{repo}/git-receive-pack: post: description: "The pack-transfer phase of a push, and the point at which a push becomes an EVENT. NEVER ANONYMOUS: a\ \ push always requires an authenticated org, and the org in the path must equal it.\n\nOnce the pack is on disk the\ \ repository's storage usage is metered and a build is fired for every branch whose tip actually moved, computed from\ \ the before/after branch diff rather than from what the client claimed. That runs on a cancel-immune context, so\ \ a client that hangs up the moment its push lands still gets its build, and it runs even when git itself exited non-zero\ \ \u2014 the refs on disk are the ground truth. Repacking housekeeping is detached and never blocks the response.\n\ \nA Content-Type other than `application/x-git-receive-pack-request` is 400. Addressed under the API prefix, so `git\ \ clone https:///v1/git//.git` works on any host the binary serves. This is git's own wire protocol,\ \ not an API call to make by hand: point a git client at the clone URL and it makes this request itself." operationId: post_git_by_org_by_repo_git-receive-pack parameters: - in: path name: org required: true schema: type: string - in: path name: repo required: true schema: type: string requestBody: content: application/octet-stream: schema: format: binary type: string summary: Accept a push, and turn it into a build tags: - git x-app: git x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/git/{org}/{repo}/git-upload-pack: post: description: "The pack-transfer phase of a clone or fetch: the request and the response are git's binary pack protocol,\ \ streamed straight through git itself \u2014 request body to git's stdin, git's stdout to the response \u2014 so\ \ a multi-gigabyte clone never lands in this process's memory.\n\nA PUBLIC repository is fetched anonymously; a private\ \ one requires its own org, and a wrong or absent org is 404 rather than a hint that the repository exists. A Content-Type\ \ other than `application/x-git-upload-pack-request` is 400. Addressed under the API prefix, so `git clone https:///v1/git//.git`\ \ works on any host the binary serves. This is git's own wire protocol, not an API call to make by hand: point a git\ \ client at the clone URL and it makes this request itself." operationId: post_git_by_org_by_repo_git-upload-pack parameters: - in: path name: org required: true schema: type: string - in: path name: repo required: true schema: type: string requestBody: content: application/octet-stream: schema: format: binary type: string summary: Serve a clone or fetch tags: - git x-app: git x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/git/{org}/{repo}/info/refs: get: description: "The ref-advertisement phase of git's smart-HTTP protocol \u2014 the first request a clone, a fetch and\ \ a push all make. `?service=` selects which: `git-upload-pack` advertises for a fetch, `git-receive-pack` for a push,\ \ and any other value is 400.\n\nANONYMOUS ONLY FOR FETCH, AND ONLY ON A PUBLIC REPOSITORY. The push advertisement\ \ always requires an authenticated org, and where a path org is present it must equal the authenticated one. A private\ \ repository reached without its org is 404, indistinguishable from one that does not exist. Addressed under the API\ \ prefix, so `git clone https:///v1/git//.git` works on any host the binary serves. This is git's\ \ own wire protocol, not an API call to make by hand: point a git client at the clone URL and it makes this request\ \ itself." operationId: get_git_by_org_by_repo_info_refs parameters: - in: path name: org required: true schema: type: string - in: path name: repo required: true schema: type: string summary: Advertise a repository's refs to a git client tags: - git x-app: git x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/graph: get: description: 'Lists the assertions recorded, every version, oldest first. It resolves nothing: a superseded claim and the one that superseded it both appear. Time: as_of keeps statements begun by that instant of the world and as_known keeps what this plane had heard by then; either absent is no bound.' operationId: graphRead parameters: - description: 'Entity narrows to what was asserted ABOUT one entity. Absent matches every entity.' example: acme/svc/api in: query name: entity required: false schema: type: string - description: Relation narrows to one relation. Absent matches every relation. in: query name: relation required: false schema: type: string - description: 'Value narrows to assertions pointing AT one value, which is how the edges into an entity are read. A property''s scalar is matched byte for byte and an edge''s value by its key, folded as every key is.' in: query name: value required: false schema: type: string - description: 'AsOf bounds the read to statements begun by an instant of the world, RFC 3339. Absent reads every instant.' in: query name: as_of required: false schema: type: string - description: 'AsKnown bounds the read to what this plane had heard by an instant, RFC 3339. Absent reads everything it holds.' example: '2026-09-01T00:00:00Z' in: query name: as_known required: false schema: type: string - description: 'Limit caps how many assertions come back. Absent, zero, or anything above the walk ceiling is the ceiling.' in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/graph.graphReadOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the assertions recorded, every version, oldest first. tags: - graph x-app: graph x-public: true x-tool: true post: description: 'Records a batch of assertions and counts what became of each. Each member is judged alone: one refusal does not discard the rest, and a member this plane already holds is a duplicate, not a refusal. A batch with nothing admitted answers 400. Time: at and until are when the statement was so, which an as_of read is bounded by; the server stamps knowable, the later of seen and its own clock, which an as_known read is bounded by. Nothing is overwritten: a correction is the statement filed again, and the version known latest speaks for it.' operationId: graphAssert requestBody: content: application/json: example: assertions: - at: '2026-09-01T00:00:00Z' confidence: 0.9 entity: acme/svc/api evidence: wiki/api#1 names: true relation: owner source: wiki/api value: acme/team/core schema: $ref: '#/components/schemas/graph.graphAssertIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/graph.graphAssertOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records a batch of assertions and counts what became of each. tags: - graph x-app: graph x-public: true x-tool: true /v1/graph/answer: post: description: 'Answers a question from the whole graph and cites the assertions it rests on. It asks a model what each community''s in-force facts say about the question, then asks once more for one answer from those findings. Every cited ID is checked to be a row of this graph, a failed model call fails the answer, and the calls are billed to the calling organization. Time: as_of and as_known place the graph that is read; either absent is now.' operationId: graphAnswer requestBody: content: application/json: example: question: Which teams own the billing services? schema: $ref: '#/components/schemas/graph.graphAnswerIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/graph.graphAnswerOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Answers a question from the whole graph and cites the assertions it rests on. tags: - graph x-app: graph x-public: true x-tool: true /v1/graph/communities: post: description: 'Partitions the edge graph into sets of entities more densely connected to each other than to the rest. The graph is the one a walk reads, direction dropped, and the partition is deterministic: the same graph at the same point numbers the same communities. Time: as_of and as_known place the graph that is partitioned; either absent is now.' operationId: graphCommunities requestBody: content: application/json: example: as_of: '2026-09-01T00:00:00Z' schema: $ref: '#/components/schemas/graph.graphCommunitiesIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/graph.graphCommunitiesOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Partitions the edge graph into sets of entities more densely connected to each other than to the rest. tags: - graph x-app: graph x-public: true x-tool: true /v1/graph/derive: post: description: "Concludes what the organization's rules derive from the graph, with a\nproof for every conclusion: the\ \ rule, and each support as the assertion in\nforce it rests on or as the derived atom it is.\n\nThe rules are assertions\ \ \u2014 (`rule:`, `rule`, ``), Datalog over\nthe organization's relations, with recursion, stratified\ \ negation and the\nbuiltins = and != \u2014 and the graph is read the way resolve reads it, so a\nretraction, an\ \ until and a declared cardinality all apply. Nothing derived is\nstored unless an admin asks to file it. Without\ \ names assertions to derive\nwithout, and the answer then says what would be lost, and what gained, if\nthey had\ \ never been filed.\n\nTime: as_of and as_known place the graph and the rules it is read with, as\nthey place a resolve;\ \ either absent is now. Filing takes neither: a filed\nconclusion holds from the instant it was derived." operationId: graphDerive requestBody: content: application/json: example: as_of: '2026-09-01T00:00:00Z' predicates: - ancestor schema: $ref: '#/components/schemas/graph.graphDeriveIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/graph.graphDeriveOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Concludes what the organization''s rules derive from the graph, with a proof for every conclusion: the rule, and each support as the assertion in force it rests on or as the derived atom it is.' tags: - graph x-app: graph x-public: true x-tool: true /v1/graph/diff: post: description: 'Reports what came into force, was superseded and was retracted between two points. Each point is resolved as resolve would, never read off the rows in between. A version that loses the order is invisible here as it is there, and one that repeats what held is agreement, not change. Time: a point is an as_of and an as_known instant, from and from_known against to and to_known. Hold the known instants equal for what happened in the world; hold the valid instants equal for what the record learned. to and to_known default to now, from to to and from_known to to_known, and the two points must differ.' operationId: graphDiff requestBody: content: application/json: example: entity: acme/svc/api from: '2026-08-01T00:00:00Z' to: '2026-09-01T00:00:00Z' schema: $ref: '#/components/schemas/graph.graphDiffIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/graph.graphDiffOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports what came into force, was superseded and was retracted between two points. tags: - graph x-app: graph x-public: true x-tool: true /v1/graph/erase: post: description: 'Removes every assertion that names an entity and returns a receipt. It requires an admin of the organization it acts in; platform sudo is not enough, so erasure is never cross-tenant. Nothing is removed while a matching assertion is under litigation hold (409) or where the erasure cannot be recorded (503). The audit trail keeps who, when, why, how many and the receipt''s digest, never the entity, and holds it before the erasure commits. Time: erasure is the one act outside the two times. The rows go at every as_of and as_known, so no past read recovers them.' operationId: graphErase requestBody: content: application/json: example: entity: acme/person/42 reason: DSR-1187, GDPR Art. 17 schema: $ref: '#/components/schemas/graph.graphEraseIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/graph.graphEraseOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes every assertion that names an entity and returns a receipt. tags: - graph x-app: graph x-public: true x-tool: true /v1/graph/extract: post: description: 'Reads the relations a source states and returns them, recording nothing. A line `relation:: value` states one; a value written `[[key]]` names an entity and makes the relation an edge. The subject is the nearest heading above the line, or the request''s subject until a heading names one. What filing would refuse is returned apart, in refused: a declaration from a caller who is not an admin of the organization, and what the declared schema refuses, checked against the store and against the types and declarations the document itself states that the caller may file. Time: it records nothing and takes no as_of or as_known; the schema is checked as of now.' operationId: graphExtract requestBody: content: application/json: example: at: '2026-09-01T00:00:00Z' source: wiki/api text: '# acme/svc/api owner:: [[acme/team/core]] tier:: 1' schema: $ref: '#/components/schemas/graph.graphSourceIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/graph.graphExtractOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads the relations a source states and returns them, recording nothing. tags: - graph x-app: graph x-public: true x-tool: true /v1/graph/graphql: post: description: "Runs a GraphQL query against this organization's assertions.\n\nIt is the one endpoint here a caller can\ \ TRAVERSE: the REST ops each answer a single question, so composing them \u2014 the entities this one points at,\ \ and what each of those resolves to \u2014 costs a request per hop with the intermediate keys held by the caller.\ \ Here that is one query and the nesting is the answer's shape.\n\nIt adds no way to ask anything new. Every field\ \ runs the SAME operation the matching REST route runs, so the tenancy, the as-of bound, the traversal bounds and\ \ the conflict rule are the ones already in force; the schema is served by introspection.\n\nA query that cannot run\ \ answers 200 with an `errors` list, which is the wire every GraphQL client parses." operationId: post_graph_graphql requestBody: content: application/json: schema: $ref: '#/components/schemas/graphQLIn' responses: 2XX: content: application/json: schema: $ref: '#/components/schemas/graphQLOut' description: Success summary: Ask the graph in one request, traversing. tags: - graph x-app: graph x-public: true /v1/graph/ingest: post: description: 'Reads a source and records what it states, through the same admission as assert. Each assertion''s evidence is the section that stated it, `#
`, so the same source at the same at records one set of rows however often it is delivered. What extract reports refused is not recorded; it is counted as refused, with its reason, in the order the document states it. A source stating no relation is refused whole. Time: at is the valid time of every assertion recorded, which an as_of read is bounded by; each becomes knowable at the write, so a read with an earlier as_known does not see it.' operationId: graphIngest requestBody: content: application/json: example: at: '2026-09-01T00:00:00Z' source: wiki/api text: '# acme/svc/api owner:: [[acme/team/core]] tier:: 1' schema: $ref: '#/components/schemas/graph.graphSourceIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/graph.graphAssertOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads a source and records what it states, through the same admission as assert. tags: - graph x-app: graph x-public: true x-tool: true /v1/graph/neighbors: post: description: 'Walks the in-force edges from a set of seeds and lists every entity reached, bounded. Only the edge that holds at the point is a hop: a superseded or retracted edge is not, and neither is a property. Time: as_of walks the graph as it stood at that instant and as_known as this plane knew it then; either absent is now.' operationId: graphNeighbors requestBody: content: application/json: example: depth: 2 relation: depends seeds: - acme/svc/api schema: $ref: '#/components/schemas/graph.graphNeighborsIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/graph.graphNeighborsOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Walks the in-force edges from a set of seeds and lists every entity reached, bounded. tags: - graph x-app: graph x-public: true x-tool: true /v1/graph/path: post: description: 'Finds the shortest chain of in-force edges from one entity to another. It is the walk behind neighbors stopped at a goal: the same bound and the same rule for which edge is in force. A causal chain is this op with relations naming the relations that mean cause, such as caused_by and influenced. Time: as_of and as_known place the graph the path is found in; either absent is now.' operationId: graphPath requestBody: content: application/json: example: direction: both from: acme/svc/api to: acme/team/core schema: $ref: '#/components/schemas/graph.graphPathIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/graph.graphPathOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Finds the shortest chain of in-force edges from one entity to another. tags: - graph x-app: graph x-public: true x-tool: true /v1/graph/resolve: post: description: 'Answers what holds for one entity and relation at a point, winner first, with every weaker account that disagreed. The winner is the holding statement begun most recently, then the strongest under the rule vocabulary names. A relation that holds many values answers each; a disagreement is reported as contested, never resolved into silence. Time: as_of is the instant of the world asked about and as_known how much this plane had heard; either absent is now.' operationId: graphResolve requestBody: content: application/json: example: as_of: '2026-09-01T00:00:00Z' entity: acme/svc/api relation: owner schema: $ref: '#/components/schemas/graph.graphResolveIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/graph.graphResolveOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Answers what holds for one entity and relation at a point, winner first, with every weaker account that disagreed. tags: - graph x-app: graph x-public: true x-tool: true /v1/graph/search: get: description: 'Finds assertions by their words, where read finds them by their keys, best match first. Every word must match, as a prefix. The answer is ordered by bm25, which weighs a word by how rare it is and a row by how much of it the words are, so a limit keeps the best matches rather than the oldest. It resolves nothing, so a match may be a claim later corrected; ask resolve about what it finds. Time: as_of and as_known bound it exactly as they bound read; either absent is no bound.' operationId: graphSearch parameters: - description: 'Q is what to look for: words, matched as prefixes, all of them required. Punctuation is text here rather than syntax, so an entity key searches as itself.' example: billing owner in: query name: q required: true schema: type: string - description: Relation narrows to one relation. Absent matches every relation. in: query name: relation required: false schema: type: string - description: 'AsOf bounds the search to statements begun by an instant of the world, RFC 3339. Absent searches every instant.' in: query name: as_of required: false schema: type: string - description: 'AsKnown bounds the search to what this plane had heard by an instant, RFC 3339. Absent searches everything it holds.' in: query name: as_known required: false schema: type: string - description: 'Limit caps how many assertions come back. Absent, zero, or anything above the walk ceiling is the ceiling.' in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/graph.graphReadOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Finds assertions by their words, where read finds them by their keys, best match first. tags: - graph x-app: graph x-public: true x-tool: true /v1/graph/vocabulary: get: description: 'Lists the relations in use, the schema declared for them and the rule that settles a conflict. Time: it takes no as_of or as_known; the schema is the one in force now.' operationId: graphVocabulary responses: '200': content: application/json: schema: $ref: '#/components/schemas/graph.graphVocabularyOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the relations in use, the schema declared for them and the rule that settles a conflict. tags: - graph x-app: graph x-public: true x-tool: true /v1/guide: get: description: 'Returns the caller org''s launch journey: the active curriculum''s version and title, every step with its state, whether it is available, what blocks it and whether the Business AI can run it, the done/total/percent progress with the next step to take, and the org''s analytics funnel folded in. Auto-detect runs first, so a step the org has already completed elsewhere reads done without anyone marking it.' operationId: get_guide responses: '200': content: application/json: schema: $ref: '#/components/schemas/guide.overviewView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the caller org''s launch journey: the active curriculum''s version and title, every step with its state, whether it is available, what blocks it and whether the Business AI can run it, the done/total/percent progress with the next step to take, and the org''s analytics funnel folded in.' tags: - guide x-app: guide x-public: true x-tool: true /v1/guide/actions: get: description: 'Returns the caller org''s Business AI action ledger, most recent first: every "do it for me" tool call, the arguments it ran with, its result and whether it succeeded. It is the audit-visible record of what the agent did on the org''s behalf, and the backing state for the "acted" auto-detect signal.' operationId: get_guide_actions responses: '200': content: application/json: schema: $ref: '#/components/schemas/guide.actionsView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the caller org''s Business AI action ledger, most recent first: every "do it for me" tool call, the arguments it ran with, its result and whether it succeeded.' tags: - guide x-app: guide x-public: true x-tool: true /v1/guide/analytics: get: description: "Returns the caller org's funnel from the analytics lens plus the GTM\nrecommendations derived from it.\ \ It is the Business AI's data-grounded read \u2014\nwhat the funnel is doing, and the next-best action to move its\ \ weakest stage. An\nunreachable or silent warehouse answers available=false, never a fabricated\nnumber." operationId: get_guide_analytics responses: '200': content: application/json: schema: $ref: '#/components/schemas/guide.analyticsView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the caller org's funnel from the analytics lens plus the GTM recommendations derived from it. tags: - guide x-app: guide x-public: true x-tool: true /v1/guide/blueprint: get: description: "Returns the FULL authored brand blueprint \u2014 every principle,\nsection, step, strategy and template\ \ WITH its enabled flag made explicit,\nincluding the disabled items the org-facing reads never see \u2014 plus the\ \ active\nversion number, the brand key it is stored under and the item counts. It is the\nSuperAdmin authoring view\ \ of the platform blueprint, so it is refused 403 for\nanyone else, including a per-org admin: the brand blueprint\ \ is shared platform\ncontent, not a per-customer surface." operationId: get_guide_blueprint responses: '200': content: application/json: schema: $ref: '#/components/schemas/guide.blueprintView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the FULL authored brand blueprint \u2014 every principle, section, step, strategy and template WITH\ \ its enabled flag made explicit, including the disabled items the org-facing reads never see \u2014 plus the active\ \ version number, the brand key it is stored under and the item counts." tags: - guide x-app: guide x-public: true x-tool: true put: description: "Replaces the deployment's brand blueprint \u2014 the shared journey, sections, strategies and templates\ \ every org starts from \u2014 as a NEW VERSION, and answers the stored document with its key and version number.\ \ The previous versions are kept, so /blueprint/versions is a real recovery trail.\n\nSuperAdmin ONLY. A per-org admin\ \ is 403: this is platform content, not a per-customer surface \u2014 the per-customer surface is /v1/guide/curriculum.\ \ The write is audited.\n\nThe body is a blueprint document accepted as YAML **or** JSON, which is the caller-visible\ \ reason it takes a raw body. It must parse AND validate \u2014 unique ids throughout, an acyclic step graph with\ \ no dangling dependencies, every step's section and every strategy's principle resolving to a real one \u2014 or\ \ it is 422 and never becomes active, leaving the version already serving authoritative. An empty body is 400 and\ \ one over 16 MiB is 413.\n\nEdits are live: the next resolve reads the newest version. A stored document that is\ \ itself corrupt or schema-drifted does not block this write \u2014 the target is resolved without parsing what is\ \ there \u2014 so a bad version can always be published over." operationId: put_guide_blueprint summary: Publish a new version of the brand blueprint tags: - guide x-app: guide x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/guide/blueprint/versions: get: description: "Returns the brand blueprint's version history \u2014 every\nstored version's number and edit time, newest\ \ first \u2014 which is the\npoint-in-time-recovery and audit trail behind the authoring plane. Metadata\nonly: the\ \ documents are not returned. SuperAdmin only, like the rest of this\nplane. The history is listable even when the\ \ current stored document no longer\nparses, so a schema-drifted row can still be diagnosed." operationId: get_guide_blueprint_versions responses: '200': content: application/json: schema: $ref: '#/components/schemas/guide.blueprintVersionsView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the brand blueprint's version history \u2014 every stored version's number and edit time, newest first\ \ \u2014 which is the point-in-time-recovery and audit trail behind the authoring plane." tags: - guide x-app: guide x-public: true x-tool: true /v1/guide/blueprint/{collection}/{id}: patch: description: "Edits a single item of the brand blueprint by id and saves it as a NEW VERSION, answering the whole blueprint\ \ after the edit. `collection` is one of `sections`, `steps`, `strategies` or `templates`; anything else is 400, and\ \ an id that collection does not hold is 404. This is also the retire lever: `{\"enabled\": false}` takes an item\ \ out of every org's journey without deleting it or its history.\n\nSuperAdmin ONLY, like the rest of the authoring\ \ plane; a per-org admin is 403. The write is audited.\n\nThe patch is a SHALLOW merge over the item's own top-level\ \ keys \u2014 a key you send replaces that key whole, a key you omit is left alone \u2014 and `id` is dropped from\ \ the patch before it is applied, so an edit can never rekey an item. That is why the body has no declarable shape:\ \ its keys are the patched item's, not this route's.\n\nFail-closed on the WHOLE document, not just the item: the\ \ blueprint is re-validated after the merge, so a patch that would dangle a dependency, break the step DAG or empty\ \ the journey is 422 and nothing is saved. An empty patch is 400 and one over 16 MiB is 413." operationId: patch_guide_blueprint_by_collection_by_id parameters: - in: path name: collection required: true schema: type: string - in: path name: id required: true schema: type: string summary: "Edit \u2014 or retire \u2014 one item of the brand blueprint" tags: - guide x-app: guide x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/guide/chat: post: description: "Answers a founder's question about their launch journey as the Business AI\ncoach: it grounds the reply\ \ in the org's REAL progress, its ranked available\nquests and its analytics funnel, and returns those candidate quests\ \ alongside so\nthe caller can act on one. READ-ONLY \u2014 it advises and never runs a step, so it\ncannot be talked\ \ into performing an action; the only executing path is POST\n/v1/guide/steps/{id}/do. One AI completion per call,\ \ billed to the caller's own\npayer." operationId: post_guide_chat requestBody: content: application/json: example: message: what should I do next to get my first customers? schema: $ref: '#/components/schemas/guide.chatRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/guide.chatResponse' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Answers a founder''s question about their launch journey as the Business AI coach: it grounds the reply in the org''s REAL progress, its ranked available quests and its analytics funnel, and returns those candidate quests alongside so the caller can act on one.' tags: - guide x-app: guide x-public: true x-tool: true /v1/guide/curriculum: delete: description: "Clears the caller org's curriculum override and returns the\njourney it falls back to \u2014 the brand\ \ blueprint, else the embedded fixture.\nClearing an org that never set one is a no-op that answers the same default." operationId: delete_guide_curriculum responses: '200': content: application/json: schema: $ref: '#/components/schemas/guide.curriculumView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Clears the caller org's curriculum override and returns the journey it falls back to \u2014 the brand blueprint,\ \ else the embedded fixture." tags: - guide x-app: guide x-public: true x-tool: true get: description: "Returns the journey the caller's org is actually running, and\nwhether it comes from the org's OWN override\ \ (custom) or from the platform\ndefault \u2014 the brand blueprint, else the embedded fixture." operationId: get_guide_curriculum responses: '200': content: application/json: schema: $ref: '#/components/schemas/guide.curriculumView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the journey the caller's org is actually running, and whether it comes from the org's OWN override\ \ (custom) or from the platform default \u2014 the brand blueprint, else the embedded fixture." tags: - guide x-app: guide x-public: true x-tool: true put: description: "Sets the caller org's OWN curriculum \u2014 the per-customer override \u2014 and answers the journey now\ \ in force with `custom: true`. The body is a curriculum document, and it is accepted as YAML **or** JSON: that is\ \ the caller-visible reason this takes a raw body rather than a declared shape. Whatever the syntax, the CANONICAL\ \ parsed form is what is stored, so the document the engine runs never depends on how it was written.\n\nFail-closed:\ \ a body that does not parse, or parses but is not a valid journey (unique step ids, no dangling or cyclic dependencies),\ \ is 422 and NEVER becomes active \u2014 the org keeps the journey it had. Requires a validated org: 401 without a\ \ principal, 403 without an org. An empty body is 400 and one over 256 KiB is 413.\n\nThis is tier one only. It overrides\ \ nothing but this org's own journey; the shared brand blueprint is a different surface with a different gate. DELETE\ \ the same path to drop the override and fall back to it." operationId: put_guide_curriculum summary: Replace your org's journey with a curriculum you author tags: - guide x-app: guide x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/guide/profile: get: description: "Returns the caller org's OBSERVED growth profile \u2014 the signal set, the\nclassified growth stage,\ \ and the org's own key metrics. It is a pure READ,\nrecomputed from the org's CURRENT state each request (real-time\ \ by pull): it\nreuses the reconcile path (snapshotFor runs the detectors) for launch progress\nand runs the growth\ \ probes (observe) for the signals \u2014 it never caches, never\nruns a billable effect, never targets another org.\ \ Org-scoped on the validated\nprincipal; fail-closed without one. It PRODUCES the profile and classifies the\nstage;\ \ it decides NO recommendation (that is a later surface)." operationId: get_guide_profile responses: '200': content: application/json: schema: $ref: '#/components/schemas/guide.profileResponse' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the caller org's OBSERVED growth profile \u2014 the signal set, the classified growth stage, and the\ \ org's own key metrics." tags: - guide x-app: guide x-public: true x-tool: true /v1/guide/steps/{id}/do: post: description: "Executes one step of the caller org's journey through that principal's OWN tool plane and answers the\ \ action log \u2014 `{step, events, state}` \u2014 so the caller sees every tool call the agent made and where the\ \ step ended up. This is the ONE executing path in guide: suggest and chat advise, this acts, and the work is charged\ \ to the calling principal's ledger.\n\nAsk for it live and the same actions arrive as Server-Sent Events instead,\ \ on either of two triggers \u2014 `Accept: text/event-stream` or `?stream=1`. The stream opens with a comment, emits\ \ one frame per action as it happens, and closes with an `end` frame carrying `ok` and the final state. The streamed\ \ run is detached and bounded at 120 seconds, so it finishes on its own clock once the response has begun.\n\nAn agent\ \ that FAILS is not a failed request: the JSON answer still comes back 200 with `error` beside the events it did manage,\ \ and the stream still ends with `ok:false`. The refusals are the ones before the agent runs \u2014 409 with `{error,\ \ step, blockedBy}` for a step whose dependencies are unfinished, 404 for an id the journey does not contain, 401\ \ without a principal and 403 without an org." operationId: post_guide_steps_by_id_do parameters: - in: path name: id required: true schema: type: string summary: Have the Business AI actually do the step for you tags: - guide x-app: guide x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/guide/steps/{id}/done: post: description: 'Marks one step of the caller org''s journey complete and returns the refreshed journey. Dependency-GATED, exactly as start is: a step whose prerequisites are unfinished is refused 409 carrying {error, step, blockedBy} naming what is in the way.' operationId: post_guide_steps_by_id_done parameters: - description: ID is the step's id, as it appears in the journey (e.g. "gsuite"). in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/guide.overviewView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Marks one step of the caller org's journey complete and returns the refreshed journey. tags: - guide x-app: guide x-public: true x-tool: true /v1/guide/steps/{id}/reset: post: description: "Returns one step of the caller org's journey to todo \u2014 clearing a\nmanual mark or a skip \u2014 and\ \ returns the refreshed journey. Reset is never\ndependency-gated. Auto-detect runs on the next read, so a step the\ \ org has in\nfact completed elsewhere goes straight back to done." operationId: post_guide_steps_by_id_reset parameters: - description: ID is the step's id, as it appears in the journey (e.g. "gsuite"). in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/guide.overviewView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one step of the caller org's journey to todo \u2014 clearing a manual mark or a skip \u2014 and returns\ \ the refreshed journey." tags: - guide x-app: guide x-public: true x-tool: true /v1/guide/steps/{id}/skip: post: description: "Marks one step of the caller org's journey skipped and returns the\nrefreshed journey. Skipping is never\ \ dependency-gated \u2014 the founder is\ndeclaring the step does not apply to them \u2014 so a step whose dependencies\ \ are\nunfinished can still be skipped, and a skipped step counts as terminal for\neverything downstream of it." operationId: post_guide_steps_by_id_skip parameters: - description: ID is the step's id, as it appears in the journey (e.g. "gsuite"). in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/guide.overviewView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Marks one step of the caller org's journey skipped and returns the refreshed journey. tags: - guide x-app: guide x-public: true x-tool: true /v1/guide/steps/{id}/start: post: description: "Marks one step of the caller org's journey in progress and returns the\nrefreshed journey.\n\nDependency-GATED:\ \ a step whose prerequisites are unfinished is refused 409\ncarrying {error, step, blockedBy}, where blockedBy names\ \ the exact steps in the\nway \u2014 enough to render the reason without asking again." operationId: post_guide_steps_by_id_start parameters: - description: ID is the step's id, as it appears in the journey (e.g. "gsuite"). in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/guide.overviewView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Marks one step of the caller org's journey in progress and returns the refreshed journey. tags: - guide x-app: guide x-public: true x-tool: true /v1/guide/strategies: get: description: "Returns the ENABLED tactics corpus for the caller's org: the tactics\nlibrary narrowed by the explicit\ \ category/workload filters AND by the org's\nOBSERVED growth stage and capability signals (a tactic's tags are\n\ preconditions, so it surfaces only once the org can act on it). Passing stage\nPREVIEWS the corpus at that stage instead\ \ of the observed one. The content is\nshared platform data \u2014 no org's records \u2014 and the read is never a\ \ billable\neffect." operationId: get_guide_strategies parameters: - description: Category filters to tactics in exactly this category. example: viral-coefficient in: query name: category required: false schema: type: string - description: 'Stage previews the corpus at a chosen growth stage (research|formed|launched|activated|scaling), overriding the org''s observed one. An unknown value is ignored and the observed stage stands.' example: scaling in: query name: stage required: false schema: type: string - description: Workload filters to tactics with exactly this workload. in: query name: workload required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/guide.corpusView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the ENABLED tactics corpus for the caller''s org: the tactics library narrowed by the explicit category/workload filters AND by the org''s OBSERVED growth stage and capability signals (a tactic''s tags are preconditions, so it surfaces only once the org can act on it).' tags: - guide x-app: guide x-public: true x-tool: true /v1/guide/suggest: get: description: "Returns the caller org's next-best quests: the available, non-terminal\nsteps of its journey ranked by\ \ how much downstream work each unblocks, each with\nthe grounded reason it is a good next move and whether the Business\ \ AI can run\nit, plus the org's funnel and the GTM recommendations derived from it. A\nbest-effort AI narrative over\ \ exactly those quests and numbers is included when\nan AI plane is wired. READ-ONLY: it advises and never runs a\ \ step \u2014 the only\nexecuting path is POST /v1/guide/steps/{id}/do." operationId: get_guide_suggest responses: '200': content: application/json: schema: $ref: '#/components/schemas/guide.suggestResponse' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the caller org''s next-best quests: the available, non-terminal steps of its journey ranked by how much downstream work each unblocks, each with the grounded reason it is a good next move and whether the Business AI can run it, plus the org''s funnel and the GTM recommendations derived from it.' tags: - guide x-app: guide x-public: true x-tool: true /v1/help/articles: get: description: 'Returns the public knowledge base: the help center''s Published, publicly-visible articles as cards. The org is server-fixed and the status/is_public filter is server-set, so neither the tenant nor the visibility can be widened by the caller. A deployment with no help center answers 404.' operationId: get_help_articles parameters: - description: 'Category narrows the list to one knowledge-base section, matched against the article''s category by exact name. Empty lists every section.' in: query name: category required: false schema: type: string - description: 'Limit caps how many articles are returned. Anything that is not a positive integer uses 50, and values above 200 are clamped to 200.' in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/help.helpArticleList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the public knowledge base: the help center''s Published, publicly-visible articles as cards.' tags: - help x-app: help x-public: true x-tool: true /v1/help/articles/{slug}: get: description: "Returns one public article by slug, with its body. A missing, Draft,\nor internal (non-public) article\ \ is 404 \u2014 fail-closed, so this route is no\nexistence oracle for anything beyond \"published and public\"." operationId: get_help_articles_by_slug parameters: - description: 'Slug is the article''s public identifier, from the path. It IS the document name in the help center''s store.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/help.helpArticle' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one public article by slug, with its body. tags: - help x-app: help x-public: true x-tool: true /v1/help/categories: get: description: "Returns the knowledge-base sections for the public center's\nnavigation \u2014 but ONLY the sections that\ \ front at least one Published, public\narticle, so an internal (agent-only) category name or description never leaks.\ \ A\nsection with no public article is invisible; a center with no public articles has\nno sections, which is an empty\ \ list rather than an error." operationId: get_help_categories responses: '200': content: application/json: schema: $ref: '#/components/schemas/help.helpCategoryList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the knowledge-base sections for the public center's navigation \u2014 but ONLY the sections that front\ \ at least one Published, public article, so an internal (agent-only) category name or description never leaks." tags: - help x-app: help x-public: true x-tool: true /v1/help/tickets: post: description: "Files a customer support ticket into the public help center. It\ncreates the ticket (status Open, source\ \ portal) with the customer's message on\nthe description, then records that same message as the opening entry of\ \ the\nticket's conversation thread; the description carries it regardless, so failing\nto write that entry loses\ \ nothing. Answers 201 with an opaque reference.\n\nA deployment with no help center answers 404, one whose center\ \ has not installed\nthe Help model answers 503, and a body over 64 KiB answers 413 \u2014 in that order,\nwhich is\ \ the order the route has always decided them in." operationId: post_help_tickets requestBody: content: application/json: schema: $ref: '#/components/schemas/help.helpTicketIntake' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/help.helpTicketFiled' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Files a customer support ticket into the public help center. tags: - help x-app: help x-public: true x-tool: true /v1/iam/.well-known/jwks: get: description: "Publishes the public keys that verify the tokens issued here \u2014 the\none URL you point a service at\ \ so it can check a token itself, offline, without\ncalling back and without holding any secret of ours.\n\nKeys appear\ \ here before they start signing and stay after they stop, so a\nrotation never leaves a live token unverifiable.\ \ Nothing private is ever\npublished." operationId: get_iam_.well-known_jwks summary: "Publishes the public keys that verify the tokens issued here \u2014 the one URL you point a service at so\ \ it can check a token itself, offline, without calling back and without holding any secret of ours." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/.well-known/oauth-authorization-server: get: description: "Returns the OpenID Connect discovery document \u2014 the one URL you\npoint a standards-compliant client\ \ at so it can find every other endpoint on\nits own, instead of you configuring them by hand.\n\nIt advertises only\ \ what is actually implemented, so a client that reads it\ncannot ask for a flow that will fail: the authorization-code\ \ flow, PKCE with\nS256, the supported grants, and the signing algorithms whose public keys the\nJWKS really publishes.\n\ \nThe issuer is derived from the host you asked on and is the same value the\ntokens carry, so a client that pins\ \ the issuer never sees it change." operationId: get_iam_.well-known_oauth-authorization-server summary: "Returns the OpenID Connect discovery document \u2014 the one URL you point a standards-compliant client at\ \ so it can find every other endpoint on its own, instead of you configuring them by hand." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/.well-known/openid-configuration: get: description: "Returns the OpenID Connect discovery document \u2014 the one URL you\npoint a standards-compliant client\ \ at so it can find every other endpoint on\nits own, instead of you configuring them by hand.\n\nIt advertises only\ \ what is actually implemented, so a client that reads it\ncannot ask for a flow that will fail: the authorization-code\ \ flow, PKCE with\nS256, the supported grants, and the signing algorithms whose public keys the\nJWKS really publishes.\n\ \nThe issuer is derived from the host you asked on and is the same value the\ntokens carry, so a client that pins\ \ the issuer never sees it change." operationId: get_iam_.well-known_openid-configuration summary: "Returns the OpenID Connect discovery document \u2014 the one URL you point a standards-compliant client at\ \ so it can find every other endpoint on its own, instead of you configuring them by hand." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/account: get: description: "Returns the signed-in person's own account and the organization\nthey belong to \u2014 what a console\ \ reads to draw the account menu.\n\nPasswords, API secrets and MFA material are stripped. It answers for a session\n\ cookie or a bearer token alike." operationId: get_iam_account summary: "Returns the signed-in person's own account and the organization they belong to \u2014 what a console reads\ \ to draw the account menu." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). put: description: "Saves the calling person's own profile \u2014 the name they are\nshown by, their picture, a line about\ \ themselves and a link.\n\nOnly their own: the request names nobody, so it cannot reach another account.\nSend only\ \ what you are changing; a field you leave out keeps the value it had,\nand a field you send empty is cleared.\n\n\ A picture is an https link or an inline image up to 96 KiB, the same value an\norganization's mark is (schema.AvatarRef)\ \ \u2014 one rule for how a subject appears,\nwhether the subject is a person or an organization." operationId: put_iam_account parameters: - in: header name: Cookie required: false schema: type: string - in: header name: Authorization required: false schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.accountBody' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: ok '400': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: bad request default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Saves the calling person's own profile \u2014 the name they are shown by, their picture, a line about themselves\ \ and a link." tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/accounts: get: description: "Returns the people signed in on this browser, the most recent\nsign-in first \u2014 what the sign-in page\ \ lists when an application asks the person\nto choose an account.\n\nIt reads the session cookie and nothing else,\ \ so it only ever answers the\nbrowser holding the sessions. An account forbidden or deleted since it signed\nin is\ \ left out. A browser with nobody signed in gets an empty list." operationId: get_iam_accounts summary: "Returns the people signed in on this browser, the most recent sign-in first \u2014 what the sign-in page lists\ \ when an application asks the person to choose an account." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/admin/applications/upsert: post: description: "Creates an application or updates it in place, so a\ndeployment can declare the applications it needs\ \ and run the same declaration\non every environment and on every redeploy.\n\nIt says which of the two it did. Leave\ \ the client secret out and the existing\none is kept \u2014 so re-running your deployment does not rotate a credential\ \ your\nrunning services are holding." operationId: upsertApplication parameters: - in: header name: Authorization required: false schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.registration' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.reply' description: ok '400': content: application/json: schema: $ref: '#/components/schemas/iam.reply' description: bad request '401': content: application/json: schema: $ref: '#/components/schemas/iam.reply' description: unauthorized '409': content: application/json: schema: $ref: '#/components/schemas/iam.reply' description: conflict '500': content: application/json: schema: $ref: '#/components/schemas/iam.reply' description: internal server error default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates an application or updates it in place, so a deployment can declare the applications it needs and run the same declaration on every environment and on every redeploy. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/admin/provision: post: description: "Sets up an account on someone's behalf \u2014 the same\nonboarding a person gets themselves, driven by\ \ one of your own services\ninstead of by them.\n\nIt authenticates as your service rather than as a person, which\ \ is why the\nperson to provision is named in the request. The setup it performs is\nidentical to self-service onboarding;\ \ there is one provisioning path, not\ntwo that can drift." operationId: post_iam_admin_provision summary: "Sets up an account on someone's behalf \u2014 the same onboarding a person gets themselves, driven by one\ \ of your own services instead of by them." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/admin/users/upsert: post: description: "Creates a person or updates them in place, so a deployment can\ndeclare the accounts it needs and re-run\ \ that declaration safely.\n\nIt DESCRIBES an account it meets and GRANTS only to one it creates: org-admin is\nnever\ \ raised on a row that already exists, and a machine identity is answered by\nname rather than adopted. Both are properties\ \ of the update itself, so a\nsteady-state reconcile \u2014 which changes neither \u2014 is unaffected.\n\nPasswords\ \ are hashed before they are stored. Leave the password out and their\ncurrent one is kept, so a redeploy never locks\ \ somebody out; send the same one\nagain and it is kept too, so a steady-state re-run is not a rotation.\n\nA SuperAdmin's\ \ password, email and phone are set when the account is created\nand kept on every run after." operationId: upsertUser parameters: - in: header name: Authorization required: false schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.person' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.reply' description: ok '400': content: application/json: schema: $ref: '#/components/schemas/iam.reply' description: bad request '401': content: application/json: schema: $ref: '#/components/schemas/iam.reply' description: unauthorized '500': content: application/json: schema: $ref: '#/components/schemas/iam.reply' description: internal server error default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a person or updates them in place, so a deployment can declare the accounts it needs and re-run that declaration safely. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/applications: get: description: "Returns the applications in one organization, newest first \u2014\neach product or site your people sign\ \ in to, with the sign-in methods and\nredirect URIs it allows." operationId: get_iam_applications parameters: - in: query name: owner required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.ApplicationListResult' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the applications in one organization, newest first \u2014 each product or site your people sign in\ \ to, with the sign-in methods and redirect URIs it allows." tags: - iam x-app: iam x-public: true x-tool: true post: description: "Registers an application in your organization \u2014 one product or site\nyour people sign in to, with\ \ its own client credentials, sign-in methods and\nallowed redirect URIs. A name already used in the organization\ \ is refused\nrather than overwritten.\n\nExported so the legacy add-application alias reuses this exact path \u2014\ \ one\ncreate, two spellings." operationId: post_iam_applications requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.Application' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Application' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Registers an application in your organization \u2014 one product or site your people sign in to, with its\ \ own client credentials, sign-in methods and allowed redirect URIs." tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/applications/{owner}/{name}: delete: description: 'Removes an application. Anyone mid-sign-in through it is turned away and its client credentials stop working, so retire the integration before deleting it.' operationId: delete_iam_applications_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.DeleteResult' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes an application. tags: - iam x-app: iam x-public: true x-tool: true get: description: 'Returns one application: its sign-in methods, its allowed redirect URIs and the client credentials your integration authenticates with.' operationId: get_iam_applications_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Application' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one application: its sign-in methods, its allowed redirect URIs and the client credentials your integration authenticates with.' tags: - iam x-app: iam x-public: true x-tool: true put: description: "Changes an application's display, its sign-in methods and the redirect\nURIs it may return to \u2014 the\ \ call that makes login work from a new host. Which\norganization it belongs to and what it is named are fixed when\ \ it is created\nand are not editable here.\n\nExported so the legacy update-application alias reuses this exact path\ \ \u2014 one\nupdate, two spellings." operationId: put_iam_applications_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.Application' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Application' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Changes an application's display, its sign-in methods and the redirect URIs it may return to \u2014 the call\ \ that makes login work from a new host." tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/assume: post: description: "Steps a platform operator into an organization: it returns their\nown access token re-scoped to that tenant,\ \ so they see what the tenant sees.\n\nThe token still names the operator \u2014 stepping in is not becoming somebody\n\ else \u2014 and records the organization it was scoped to, so everything done with\nit is attributed to the person\ \ who did it. Only a platform operator may, and\nthe attempt is recorded whether or not it succeeds." operationId: post_iam_assume parameters: - in: header name: Authorization required: false schema: type: string - in: header name: X-Forwarded-For required: false schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.assumeBody' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: ok '400': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: bad request '401': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: unauthorized '403': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: forbidden '404': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: not found default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Steps a platform operator into an organization: it returns their own access token re-scoped to that tenant, so they see what the tenant sees.' tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/audit-logs: get: description: "Returns your organization's audit trail, newest first \u2014 who did\nwhat, when, and from where. It is\ \ the record you reach for during a security\nreview or an incident.\n\nYou see your own organization's audit trail\ \ and no one else's; which organization that\nis comes from your credentials, not from the request." operationId: get_iam_audit-logs parameters: - in: query name: owner required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.auditlogs.ListOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns your organization's audit trail, newest first \u2014 who did what, when, and from where." tags: - iam x-app: iam x-public: true x-tool: true post: description: 'Records an audit entry, so activity from your own systems lands in the same trail as everything the Hanzo Cloud records for you.' operationId: post_iam_audit-logs requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.auditlogs.Input' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.AuditLog' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records an audit entry, so activity from your own systems lands in the same trail as everything the Hanzo Cloud records for you. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/audit-logs/{owner}/{name}: delete: description: 'Removes an audit entry. Retention policy is normally what should expire a trail; deleting by hand leaves a gap a reviewer will notice.' operationId: delete_iam_audit-logs_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.auditlogs.DeleteOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes an audit entry. tags: - iam x-app: iam x-public: true x-tool: true get: description: 'Returns one audit entry in full: the action, the person or key behind it, and the request it came in on.' operationId: get_iam_audit-logs_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.AuditLog' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one audit entry in full: the action, the person or key behind it, and the request it came in on.' tags: - iam x-app: iam x-public: true x-tool: true put: description: "Corrects an audit entry. The trail is append-only in normal operation\nand nothing in the Hanzo Cloud\ \ rewrites it \u2014 this exists for an administrator\nto correct an entry their own systems recorded wrongly." operationId: put_iam_audit-logs_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.auditlogs.Input' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.AuditLog' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Corrects an audit entry. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/auth/application: get: description: 'Returns everything a login screen needs to draw itself for one application: its branding, and each sign-in method it offers with the provider details that method needs. The client secret is masked. Read before anyone has signed in, so it carries only what is safe for a browser to see.' operationId: get_iam_auth_application parameters: - description: "ClientId is the application's OAuth client id \u2014 the one field that selects\nwhich login screen\ \ this is." in: query name: clientId required: false schema: type: string - description: 'ResponseType is the OAuth response type the screen will ask for. Only "code" is served; anything else is refused here rather than at the authorize leg, where the person has already typed a password.' in: query name: responseType required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: ok '400': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: bad request default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns everything a login screen needs to draw itself for one application: its branding, and each sign-in method it offers with the provider details that method needs.' tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/auth/methods: get: description: 'Returns the sign-in methods one application actually has switched on, so a login screen can render the right buttons for it without you hard-coding a list that drifts the moment you add a provider. Public by design: it is read before anyone has signed in, and it exposes only which methods exist, never their credentials.' operationId: get_iam_auth_methods parameters: - description: ClientId is the application's OAuth client id. in: query name: clientId required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: ok '400': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: bad request default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the sign-in methods one application actually has switched on, so a login screen can render the right buttons for it without you hard-coding a list that drifts the moment you add a provider. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/certs: get: description: "Returns your organization's signing certificates, newest first \u2014 the keys\nthe tokens your applications\ \ verify are signed with. Private key material is\nmasked.\n\nYou see your own organization's certificates and no\ \ one else's; which\norganization that is comes from your credentials, not from the request, so a\nquery parameter\ \ can never widen the listing." operationId: get_iam_certs parameters: - in: query name: owner required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.certs.ListOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns your organization's signing certificates, newest first \u2014 the keys the tokens your applications\ \ verify are signed with." tags: - iam x-app: iam x-public: true x-tool: true post: description: "Adds a signing certificate your applications can verify tokens against\n\u2014 the call you make to stage\ \ the next one before a rotation. A name already\nused in your organization is refused.\n\nIt registers the certificate's\ \ IDENTITY: its name (which is the JWKS `kid`),\nits algorithm, its expiry. Key material does not travel this way\ \ and cannot:\nthe private key is not part of the Cert's JSON, so it is neither served here\nnor accepted here. It\ \ is supplied to the process by the deployment, under the\nname registered here (internal/keyring). Staging a rotation\ \ is therefore two\nhalves \u2014 this call names the key, and the deployment provides it." operationId: post_iam_certs requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.Cert' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Cert' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Adds a signing certificate your applications can verify tokens against \u2014 the call you make to stage the\ \ next one before a rotation." tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/certs/{owner}/{name}: delete: description: 'Removes a signing certificate. Tokens signed with it can no longer be verified, so retire it only once nothing is still presenting them.' operationId: delete_iam_certs_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.certs.DeleteOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a signing certificate. tags: - iam x-app: iam x-public: true x-tool: true get: description: "Returns one signing certificate \u2014 its algorithm, its validity window and\nits public half. The private\ \ key is masked." operationId: get_iam_certs_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Cert' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one signing certificate \u2014 its algorithm, its validity window and its public half." tags: - iam x-app: iam x-public: true x-tool: true put: description: "Changes a signing certificate's settings. What it is called does not\nchange, and neither does when it\ \ was added.\n\nA PUT here is a METADATA edit \u2014 display name, expiry, provider. It overlays\nonly the fields\ \ the request actually SET onto the loaded row: a field the JSON\nomits (or leaves at its zero value) keeps what the\ \ row holds, rather than\nblanking it. That is load-bearing, not a nicety. A read serves the public\nCertificate (Mask\ \ hides only PrivateKey and AccessSecret), so a client that\nreads a cert, changes one field, and writes it back sends\ \ the masked halves\nempty and every other field it did not touch at its zero value \u2014 and the old\nfull-struct\ \ overlay wrote all of those blanks back. Blanking CryptoAlgorithm\nalone drops the cert from the JWKS (oidc.Publishes\ \ turns false), so every\ntoken under its `kid` stops verifying; blanking Provider/Account/ExpireTime\nbreaks ACME\ \ renewal and expiry \u2014 all from a request that only meant to rename\nit. Absent-or-zero means \"unchanged\",\ \ so the deployment (key) and a rotation\n(cert) remain the only way key or published material changes; the metadata\ \ API\ncannot clear it.\n\nThe overlay is generic \u2014 it copies every set field, so a field nobody has added\n\ yet is carried without a line here \u2014 and leaves three things the request may\nnot move: the bound Model (id,\ \ createdAt, key, snapshot), the natural key\n(owner/name address the row, they do not mutate it), and the creation\ \ stamp." operationId: put_iam_certs_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.Cert' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Cert' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes a signing certificate's settings. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/consent: get: description: "Returns the calling person's own privacy and communication\nchoices. Somebody who has never set them gets\ \ the defaults rather than\nnothing, so a consent screen always has something to show \u2014 insights on, and\ntraining\ \ UNANSWERED, which is the state that means the screen still has to ask." operationId: get_iam_consent summary: Returns the calling person's own privacy and communication choices. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). put: description: "Records the calling person's privacy and communication\nchoices. Only their own \u2014 there is no way\ \ to set consent for somebody else.\n\nSend only the answers you are changing. A question you leave out keeps the\n\ answer it already had, so a screen that saves one switch never revokes the\nother, and two screens saving at once\ \ do not undo each other.\n\nAn answer this version does not recognize is refused here rather than stored,\nso nothing\ \ is ever persisted for a later reader to have to interpret." operationId: put_iam_consent summary: Records the calling person's privacy and communication choices. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/delete-membership: post: description: "Takes away a person's or an application's right to act in an\norganization. Their account survives; what\ \ ends is their access to that\norganization. Revoking a membership that is already gone reports that nothing\nwas\ \ removed rather than failing, so a retry is safe. It is the mirror of ensure and takes the SAME gate:\nrevoking membership\ \ is the org's authority to give or take, so a SuperAdmin, an\nadmin of the org itself, or an org-admin-capable confidential\ \ client. Idempotent\nthrough the store \u2014 deleting an absent membership reports removed=false, never an\nerror\ \ \u2014 so a retried revoke is safe." operationId: post_iam_delete-membership summary: Takes away a person's or an application's right to act in an organization. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/invitations: get: description: "Returns your organization's invitations, newest first \u2014 who has\nbeen asked to join, on what terms,\ \ and how many seats each invitation still\nhas left.\n\nYou see the invitations of the organization your credentials\ \ run, and no one\nelse's: your own, or one you own or administer." operationId: get_iam_invitations parameters: - in: query name: owner required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.invitations.ListOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns your organization's invitations, newest first \u2014 who has been asked to join, on what terms, and\ \ how many seats each invitation still has left." tags: - iam x-app: iam x-public: true x-tool: true post: description: "Issues an invitation to join your organization \u2014 the code or link a new\nmember redeems, with the\ \ role they arrive holding and the date it stops\nworking. A name already used in the organization is refused." operationId: post_iam_invitations requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.invitations.Input' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Invitation' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Issues an invitation to join your organization \u2014 the code or link a new member redeems, with the role\ \ they arrive holding and the date it stops working." tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/invitations/accept: post: description: "Joins the caller to an organization through an invitation, for\na person who already has an account. Signing\ \ up through the invitation is the\nother way in (signupHandler); this one spends the same seat under the same rules.\n\ \nOnly the caller joins, as a member and never more; the request names nobody\nelse. An invitation pinned to an address\ \ admits only the account holding that\naddress, and only with a code IAM sent to it for this join \u2014 the account's\ \ own\nverified flag is not enough, because a tenant's identity provider can set it. An\ninvitation pinned to a phone\ \ number or a username admits no other org's account\nthis way. Joining an org the caller already belongs to succeeds\ \ and spends\nnothing. Every refusal is recorded, and an account refused acceptLimit times in\nacceptWindow is refused\ \ before anything is looked at." operationId: post_iam_invitations_accept parameters: - in: header name: Cookie required: false schema: type: string - in: header name: Authorization required: false schema: type: string - in: header name: Sec-Fetch-Site required: false schema: type: string - in: header name: Content-Type required: false schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.acceptBody' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: ok '400': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: bad request '403': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: forbidden '429': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: too many requests '502': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: bad gateway '503': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: service unavailable default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Joins the caller to an organization through an invitation, for a person who already has an account. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/invitations/{owner}/{name}: delete: description: 'Withdraws an invitation. It stops being redeemable at once; anyone who already joined through it keeps their account.' operationId: delete_iam_invitations_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.invitations.DeleteOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Withdraws an invitation. tags: - iam x-app: iam x-public: true x-tool: true get: description: 'Returns one invitation: who it is for, what it grants on acceptance, and when it expires.' operationId: get_iam_invitations_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Invitation' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one invitation: who it is for, what it grants on acceptance, and when it expires.' tags: - iam x-app: iam x-public: true x-tool: true put: description: "Changes an invitation's terms \u2014 the role it grants, how many may redeem\nit, or when it expires.\ \ What it is called does not change." operationId: put_iam_invitations_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.invitations.Input' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Invitation' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Changes an invitation's terms \u2014 the role it grants, how many may redeem it, or when it expires." tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/invitations/{owner}/{name}/send: post: description: "Emails an invitation to the address it is pinned to: who invited them, to\nwhich organization, and the\ \ link that joins them.\n\nThe caller chooses nothing about how it goes out. It is sent through the\nplatform application\ \ the caller's access token was issued to, from that\napplication's org's email account, with a link on the identity\ \ host that issued\nthe token \u2014 the way the inviter came in. Only the pinned address receives it." operationId: post_iam_invitations_by_owner_by_name_send parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string - in: header name: Authorization required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.SendOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Emails an invitation to the address it is pinned to: who invited them, to which organization, and the link that joins them.' tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/keys: get: description: "Returns an organization's API keys, newest first \u2014 what each is called,\nwhat it may reach, and its\ \ publishable half. Secret halves are never listed.\n\nWhich organization comes from your credentials, not from the\ \ request: you read\nyour own and no one else's. The capability that admits a confidential client to\nthis collection\ \ does not itself name a tenant, so the tenant is decided here." operationId: get_iam_keys parameters: - in: query name: owner required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.keys.ListResponse' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns an organization's API keys, newest first \u2014 what each is called, what it may reach, and its publishable\ \ half." tags: - iam x-app: iam x-public: true x-tool: true post: description: "Issues an API key. A standard key comes back as a publishable half you\nmay ship in client code and a\ \ secret half you must not \u2014 the secret is shown\nonce, at creation, and cannot be retrieved afterwards. A publish-scoped\ \ key is\nissued with the publishable half only, so there is no secret to leak.\n\nA name already used in your organization\ \ is refused rather than reissued, so\ncreating twice never silently invalidates a key that is in production." operationId: post_iam_keys requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.Key' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Key' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Issues an API key. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/keys/org: get: description: "Answers which organization a publishable key belongs to \u2014 what a service calls to attribute a request\ \ that arrived carrying a key shipped in a browser. This is the noun spelling of `/v1/iam/resolve-key`, the same handler\ \ at the address that replaces it; both answer while callers migrate.\n\nIt names an ORGANIZATION and never a person.\ \ No path through it loads or returns a user, so a key placed in client code cannot become a way to learn who anyone\ \ is \u2014 which is the whole reason this is a separate endpoint from the one below.\n\nA key that is expired, secret\ \ rather than publishable, or simply unknown all answer with the same sentence and a `code` saying which it was. Only\ \ a confidential service that has already proved it may resolve keys reads that code \u2014 there is no anonymous\ \ caller here to probe which keys exist \u2014 and telling those apart is what lets a holder be told to re-mint an\ \ expired key instead of hunting a configuration error." operationId: get_iam_keys_org summary: Resolve a PUBLISHABLE key to the organization that owns it tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/keys/principal: get: description: "Answers who a secret key belongs to \u2014 the owner and name a gateway needs to attribute and bill a\ \ request that arrived carrying an `sk-`. This is the noun spelling of `/v1/iam/get-user?accessKey=`, the same handler\ \ at the address that replaces it; both answer while callers migrate.\n\nIt resolves a KEY and nothing else. The verb\ \ it replaces also reads a user by `?id=`, and carrying that here would make this a second address for the user read\ \ \u2014 the exact thing being retired. Ask for a person by name at the user read; ask here only what a credential\ \ resolves to.\n\nRequires a confidential caller: the resolver authenticates as an app, so a request without that\ \ credential resolves nothing rather than falling back to an anonymous lookup. An unresolvable key answers with a\ \ `code` distinguishing expired from wrong-endpoint from unknown, so the holder can be told which one cure applies." operationId: get_iam_keys_principal summary: Resolve a SECRET key to the principal it authenticates tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/keys/{owner}/{name}: delete: description: 'Revokes an API key. Anything still presenting it stops being authorized at once, so roll the replacement out before you revoke.' operationId: delete_iam_keys_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.keys.DeleteResponse' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Revokes an API key. tags: - iam x-app: iam x-public: true x-tool: true get: description: 'Returns one API key: what it is called, what it may reach, and when it was issued.' operationId: get_iam_keys_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Key' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one API key: what it is called, what it may reach, and when it was issued.' tags: - iam x-app: iam x-public: true x-tool: true put: description: "Changes what a key is called or what it may reach. The credential\nitself is not reissued \u2014 the key\ \ in your deployment keeps working." operationId: put_iam_keys_by_owner_by_name parameters: - description: Owner is the tenant that holds the key; Name is unique within Owner. in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.Key' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Key' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes what a key is called or what it may reach. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/link: post: description: "Starts connecting another sign-in identity to the account you are already\nsigned in as. It answers with\ \ the provider's URL for the browser to follow; when\nthe provider returns, that identity is attached and you come\ \ back to returnUri.\n\nYour account is fixed here, from the credential you are already holding, and is\ncarried server-side\ \ for the rest of the round-trip \u2014 so nothing that happens at\nthe provider can point the link at somebody else." operationId: post_iam_link summary: Starts connecting another sign-in identity to the account you are already signed in as. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/linked-accounts: get: description: "Returns the sign-in identities linked to the calling\nperson's account \u2014 every provider they can\ \ currently sign in with. It is what\na security page lists next to the option to disconnect one." operationId: get_iam_linked-accounts summary: "Returns the sign-in identities linked to the calling person's account \u2014 every provider they can currently\ \ sign in with." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/login: post: description: "Signs a person in with the credential they typed, and \u2014 when the\nrequest is part of an OAuth flow\ \ \u2014 hands back the one-time code that finishes\nit. A second factor, if the account has one, is asked for and\ \ required here.\n\nThe password is compared against a stored one-way hash and is never logged,\nechoed or stored\ \ as typed." operationId: post_iam_login summary: "Signs a person in with the credential they typed, and \u2014 when the request is part of an OAuth flow \u2014\ \ hands back the one-time code that finishes it." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/memberships: get: description: "Answers either question about who belongs where: which organizations one\nperson can act in, or who can\ \ act in one organization.\n\nBoth are org-scoped: a non-SuperAdmin may read the roster of an org it belongs\nto,\ \ or the tenancy of a person whose account it administers, and nothing else.\nThe bound comes from the verified credential\ \ via principal.ScopeRead, so a\nrequest parameter can never widen it \u2014 a membership row names who may act and\n\ spend in an org, so a roster read by a stranger is a customer roster leak." operationId: get_iam_memberships parameters: - description: "User is \"/\" \u2014 which organizations that identity may act in." in: query name: user required: false schema: type: string - description: "Org is an organization \u2014 who may act in it." in: query name: org required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '400': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: bad request default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Answers either question about who belongs where: which organizations one person can act in, or who can act in one organization.' tags: - iam x-app: iam x-public: true x-tool: true post: description: "Lets a person or an application act in an organization. It is the grant\nbehind \"add someone to the team\"\ , and it is safe to repeat \u2014 granting a\nmembership that already exists changes nothing. Granting membership\ \ IS the org's authority to give, so it takes the\nsame gate a write to that org's own registry row takes: a SuperAdmin,\ \ an admin\nof the org itself, or an org-admin-capable confidential client. One rule, one\nplace (internal/authz)." operationId: post_iam_memberships summary: Lets a person or an application act in an organization. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/mfa: delete: description: "Turns a factor off, so sign-in stops asking for it. Naming no factor turns\noff ALL of them \u2014 the\ \ reset path. People may do this for themselves; doing it for\nsomebody else takes an administrator, which is what\ \ makes it the way back in when a\nphone is lost.\n\nThe recovery codes go with the last factor: they are the way\ \ past a challenge, so\nkeeping them alive for an account with nothing to challenge would leave a standing\ncredential\ \ behind." operationId: delete_iam_mfa summary: Turns a factor off, so sign-in stops asking for it. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/mfa/preferred: post: description: "Picks which second factor an account is asked for first when it has\nmore than one. Only a factor the\ \ account actually holds: storing an unheld one told\nthe login gate \"MFA is on\" \u2014 factor.Enabled reads that\ \ column \u2014 while leaving it\nnothing to ask for, so the sign-in required the password alone." operationId: post_iam_mfa_preferred summary: Picks which second factor an account is asked for first when it has more than one. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/mfa/setup/enable: post: description: "Finishes the enrolment: from here the account's sign-ins ask for this factor.\nIt requires the proof initiate\ \ handed out \u2014 a passcode from the authenticator, or the\ncode that was sent \u2014 and verifies it BEFORE writing\ \ anything.\n\nVerifying BEFORE writing is what keeps a client that never completed the proof \u2014\na skipped verify\ \ step, a QR scanned into the wrong app, a bug \u2014 from switching on\na factor no code can satisfy. That would\ \ lock the account out with no\nself-service way back: the gate holds the sign-in before minting, so the person\n\ could not obtain the bearer that disable requires.\n\nThe recovery codes are minted here and returned ONCE, on the\ \ first factor the\naccount adds. Answering with them is the way back in when no factor can be\nproduced, so they\ \ are the same value the row's digests were made from \u2014 by\nconstruction, not by a client echoing them back." operationId: post_iam_mfa_setup_enable summary: 'Finishes the enrolment: from here the account''s sign-ins ask for this factor.' tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/mfa/setup/initiate: post: description: "Starts enrolling a factor and hands over whatever the person needs to prove\nthey hold it:\n\n\tapp \ \ a fresh secret and the otpauth:// URL to render as a QR code\n\tsms a code texted to the number on the account\n\ \temail a code mailed to the address on the account\n\nNothing is switched on yet, so abandoning this step leaves\ \ the account exactly as\nit was. Response: {status:\"ok\", data:{mfaType, secret, url}} \u2014 secret and url only\n\ for the authenticator." operationId: post_iam_mfa_setup_initiate summary: 'Starts enrolling a factor and hands over whatever the person needs to prove they hold it: app a fresh secret and the otpauth:// URL to render as a QR code sms a code texted to the number on the account email a code mailed to the address on the account Nothing is switched on yet, so abandoning this step leaves the account exactly as it was.' tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/oauth/authorize: get: description: "Starts a sign-in \u2014 the address you send a browser to, and the\nbeginning of every OAuth and OpenID\ \ Connect flow.\n\nIf the person is ALREADY signed in here, it does not ask them again: it\nreturns them to the application\ \ with a one-time code and they never see this\npage. Otherwise it shows the right way to sign in for the application\ \ they are\nsigning in to, or hands off to another identity provider if that is what they\npick.\n\nA client can say\ \ what it wants with `prompt`: `none` means answer without any\nscreen at all \u2014 with the code if a session exists,\ \ with an error if not, but\nnever with a page; `login` means ask for the password again even if a session\nexists;\ \ `select_account` means let the person choose among the accounts signed\nin on this browser, or sign in to another.\ \ `login_hint` names which of those\naccounts the request is for.\n\nIt returns only to an address the application\ \ has registered. That check\nhappens before anything else, so a request naming an unregistered address is\nrefused\ \ where the person can see it rather than being bounced onwards." operationId: get_iam_oauth_authorize summary: "Starts a sign-in \u2014 the address you send a browser to, and the beginning of every OAuth and OpenID Connect\ \ flow." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). post: description: "Starts a sign-in \u2014 the address you send a browser to, and the\nbeginning of every OAuth and OpenID\ \ Connect flow.\n\nIf the person is ALREADY signed in here, it does not ask them again: it\nreturns them to the application\ \ with a one-time code and they never see this\npage. Otherwise it shows the right way to sign in for the application\ \ they are\nsigning in to, or hands off to another identity provider if that is what they\npick.\n\nA client can say\ \ what it wants with `prompt`: `none` means answer without any\nscreen at all \u2014 with the code if a session exists,\ \ with an error if not, but\nnever with a page; `login` means ask for the password again even if a session\nexists;\ \ `select_account` means let the person choose among the accounts signed\nin on this browser, or sign in to another.\ \ `login_hint` names which of those\naccounts the request is for.\n\nIt returns only to an address the application\ \ has registered. That check\nhappens before anything else, so a request naming an unregistered address is\nrefused\ \ where the person can see it rather than being bounced onwards." operationId: post_iam_oauth_authorize summary: "Starts a sign-in \u2014 the address you send a browser to, and the beginning of every OAuth and OpenID Connect\ \ flow." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/oauth/callback: get: description: "Completes the round-trip: it resolves and burns the\nsingle-use transaction (checking expiry + browser\ \ binding), exchanges and\nverifies the IdP response, links or provisions the local user, and mints the\niam authorization\ \ code the relying party expects \u2014 then redirects to the\noriginal redirect_uri with code + state." operationId: get_iam_oauth_callback summary: "Completes the round-trip: it resolves and burns the single-use transaction (checking expiry + browser binding),\ \ exchanges and verifies the IdP response, links or provisions the local user, and mints the iam authorization code\ \ the relying party expects \u2014 then redirects to the original redirect_uri with code + state." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/oauth/device: post: description: "Starts a sign-in on a device with no browser and no keyboard \u2014\na TV, a CLI, a headless box. It returns\ \ a short code to show the person and\nthe address to send them to on a phone or laptop.\n\nNothing is granted until\ \ a human approves it there; until then the code is\njust a pending request." operationId: post_iam_oauth_device summary: "Starts a sign-in on a device with no browser and no keyboard \u2014 a TV, a CLI, a headless box." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/oauth/device/info: post: description: "Answers \"what am I approving?\" for a pending device code.\n\nThe approval page exists to tell a human\ \ WHICH application they are authorizing;\na page that names any other one defeats the control it implements. The\ \ client is\na property of the CODE, not of the page or of whatever app the browser happens\nto be signed in to, so\ \ it is read from the code's row here and nowhere else.\n\nRequires a signed-in session, and answers with the same\ \ ONE opaque refusal\napproveDevice uses. That is deliberate: the user_code is only 40 bits and is the\none secret\ \ in this flow, so an unauthenticated lookup \u2014 or one that\ndistinguished unknown from expired from already-approved\ \ \u2014 would be an oracle\nfor hunting live codes. Gated and opaque, it reveals strictly less than the\napproval\ \ the same caller could already attempt." operationId: post_iam_oauth_device_info summary: Answers "what am I approving?" for a pending device code. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/oauth/federation/mfa: post: description: 'Completes a sign-in that came in through another identity provider and still owes a second factor. The person supplies the factor here and the login finishes. The account is fixed when the challenge is issued, not by the request, so no one can redirect a half-finished login onto somebody else''s account. A wrong factor uses the challenge up: retrying means starting the sign-in again, the same as a mistyped password.' operationId: post_iam_oauth_federation_mfa summary: Completes a sign-in that came in through another identity provider and still owes a second factor. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/oauth/introspect: post: description: "Answers whether an access token is still good, and what it\nis good for \u2014 the check a resource server\ \ of yours makes before honouring a\ntoken it did not mint.\n\nA token counts as active only if it verifies AND has\ \ not been revoked, so a\nrevoked token reads as dead here immediately rather than until it expires. A\ntoken that\ \ is unknown, expired or revoked answers simply that it is not\nactive, and nothing more \u2014 the endpoint is not\ \ a way to learn about tokens you\nwere not given." operationId: post_iam_oauth_introspect summary: "Answers whether an access token is still good, and what it is good for \u2014 the check a resource server\ \ of yours makes before honouring a token it did not mint." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/oauth/logout: get: description: "Ends a sign-in and sends the browser somewhere sensible. Accepts\nGET or POST, so it works as a plain\ \ link.\n\nIt ACTUALLY signs you out \u2014 worth stating, because a logout that computes a\nredirect and answers\ \ {\"status\":\"ok\"} while ending no session and revoking no\ntoken is worse than none: the person on the shared\ \ machine believes it worked.\nThree things happen here, in this order:\n\n 1. The browser session dies \u2014 sid\ \ revoked server-side AND the cookie expired\n (sessions.Clear). Server-side revocation is the load-bearing half:\ \ a copy\n of the cookie taken before logout must not still resolve.\n 2. The relying party's tokens are revoked\ \ when an id_token_hint names it, so\n the refresh token cannot mint a fresh access token after the human left.\n\ \ Revocation state is authoritative \u2014 a JWT's `exp` still reads valid for\n days, so expiry is necessary\ \ but never sufficient.\n 3. Only then is a redirect considered, and only to a REGISTERED uri.\n\nThe open-redirect\ \ guard: a redirect happens only when a VERIFIED\nid_token_hint (or client_id) identifies the application and that\ \ application\nhas registered the target. Nobody can turn your logout link into a redirect to\na site of their choosing.\n\ \nA GET is the end-session navigation (OIDC RP-Initiated Logout 1.0 \xA72), so it\nalways ends on a page: the registered\ \ address when one was asked for, else\nthis issuer's sign-in page for the application being signed out of. A relying\n\ party therefore needs no configuration to sign someone out. A POST is also how\na program signs out, so it keeps the\ \ JSON answer unless it asks for HTML." operationId: get_iam_oauth_logout summary: Ends a sign-in and sends the browser somewhere sensible. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). post: description: "Ends a sign-in and sends the browser somewhere sensible. Accepts\nGET or POST, so it works as a plain\ \ link.\n\nIt ACTUALLY signs you out \u2014 worth stating, because a logout that computes a\nredirect and answers\ \ {\"status\":\"ok\"} while ending no session and revoking no\ntoken is worse than none: the person on the shared\ \ machine believes it worked.\nThree things happen here, in this order:\n\n 1. The browser session dies \u2014 sid\ \ revoked server-side AND the cookie expired\n (sessions.Clear). Server-side revocation is the load-bearing half:\ \ a copy\n of the cookie taken before logout must not still resolve.\n 2. The relying party's tokens are revoked\ \ when an id_token_hint names it, so\n the refresh token cannot mint a fresh access token after the human left.\n\ \ Revocation state is authoritative \u2014 a JWT's `exp` still reads valid for\n days, so expiry is necessary\ \ but never sufficient.\n 3. Only then is a redirect considered, and only to a REGISTERED uri.\n\nThe open-redirect\ \ guard: a redirect happens only when a VERIFIED\nid_token_hint (or client_id) identifies the application and that\ \ application\nhas registered the target. Nobody can turn your logout link into a redirect to\na site of their choosing.\n\ \nA GET is the end-session navigation (OIDC RP-Initiated Logout 1.0 \xA72), so it\nalways ends on a page: the registered\ \ address when one was asked for, else\nthis issuer's sign-in page for the application being signed out of. A relying\n\ party therefore needs no configuration to sign someone out. A POST is also how\na program signs out, so it keeps the\ \ JSON answer unless it asks for HTML." operationId: post_iam_oauth_logout summary: Ends a sign-in and sends the browser somewhere sensible. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/oauth/refresh_token: post: description: "Exchanges what your application is holding for the tokens it\nneeds \u2014 the one-time code from a finished\ \ sign-in, a refresh token, or your\nown client credentials when the caller is a program rather than a person.\n\n\ A refresh returns a NEW refresh token and retires the one you sent. If a\nretired one is ever presented again the\ \ whole chain is revoked, on the\nassumption that a token which came back from the dead was copied \u2014 so a stolen\n\ refresh token buys an attacker one use and costs them the session.\n\nResponses are never cached, by any hop." operationId: post_iam_oauth_refresh_token summary: "Exchanges what your application is holding for the tokens it needs \u2014 the one-time code from a finished\ \ sign-in, a refresh token, or your own client credentials when the caller is a program rather than a person." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/oauth/revoke: post: description: "Retires a token before it expires \u2014 what you call when someone\nsigns out or a credential may have\ \ leaked.\n\nRevoking an access token kills that token. Revoking a REFRESH token kills the\nwhole chain it belongs\ \ to, so no further access tokens can be minted from it\nand every token already minted from it dies with it.\n\n\ A token that is not yours, or that never existed, answers success and does\nnothing \u2014 so the endpoint cannot\ \ be used to discover which tokens are real.\n\nPUBLIC clients revoke too, and must: sign-out is the only control\ \ a long-lived\nrefresh token has (RFC 7009 \xA72.1 \u2014 a public client identifies itself with\nclient_id). A browser\ \ app or CLI is a public PKCE client and holds no secret,\nand that includes the public half of a registration that\ \ keeps a secret for a\nbackend path, so requiring one here would leave signing out as a local delete \u2014\nforgetting\ \ a credential that stays spendable for the rest of its lifetime.\n\nWidening authentication does not widen authority.\ \ The caller must still POSSESS\nthe token \u2014 and possession already permits USE, of which revocation is the\n\ strict opposite \u2014 and the row must belong to the client that presents it. A\ncaller that did not prove the registration's\ \ secret revokes only a grant that\nwas itself established without it, so a public client_id buys the ability to\n\ destroy exactly what its holder could otherwise spend." operationId: post_iam_oauth_revoke summary: "Retires a token before it expires \u2014 what you call when someone signs out or a credential may have leaked." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/oauth/token: post: description: "Exchanges what your application is holding for the tokens it\nneeds \u2014 the one-time code from a finished\ \ sign-in, a refresh token, or your\nown client credentials when the caller is a program rather than a person.\n\n\ A refresh returns a NEW refresh token and retires the one you sent. If a\nretired one is ever presented again the\ \ whole chain is revoked, on the\nassumption that a token which came back from the dead was copied \u2014 so a stolen\n\ refresh token buys an attacker one use and costs them the session.\n\nResponses are never cached, by any hop." operationId: post_iam_oauth_token summary: "Exchanges what your application is holding for the tokens it needs \u2014 the one-time code from a finished\ \ sign-in, a refresh token, or your own client credentials when the caller is a program rather than a person." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/oauth/userinfo: get: description: "Returns the profile claims for whoever the access token\nbelongs to \u2014 the standard OpenID Connect\ \ way to find out who is calling you\nwithout your application storing anything itself.\n\nThe token must still be\ \ live: revoke it and this stops answering." operationId: get_iam_oauth_userinfo summary: "Returns the profile claims for whoever the access token belongs to \u2014 the standard OpenID Connect way\ \ to find out who is calling you without your application storing anything itself." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). post: description: "Returns the profile claims for whoever the access token\nbelongs to \u2014 the standard OpenID Connect\ \ way to find out who is calling you\nwithout your application storing anything itself.\n\nThe token must still be\ \ live: revoke it and this stops answering." operationId: post_iam_oauth_userinfo summary: "Returns the profile claims for whoever the access token belongs to \u2014 the standard OpenID Connect way\ \ to find out who is calling you without your application storing anything itself." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/onboard: post: description: "Finishes setting up the account of whoever is calling \u2014 it\ncreates their organization if they have\ \ none and puts them in it, so a person\nwho has just signed up lands somewhere they can work.\n\nIt always acts on\ \ the caller and never on somebody named in the request, so\nthere is no way to onboard another person's account through\ \ it." operationId: post_iam_onboard summary: "Finishes setting up the account of whoever is calling \u2014 it creates their organization if they have none\ \ and puts them in it, so a person who has just signed up lands somewhere they can work." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/organizations: get: description: "Returns the organizations you can act in, the ones you belong to first\nand the rest after, newest first,\ \ narrowed by an optional query against the\nname or the display name.\n\nPlatform operators see every organization;\ \ everyone else sees their own. Pass\nthe cursor from the previous page to continue; an empty cursor in the answer\n\ means there is nothing more.\n\nTHE SCOPE IS THE HANDLER'S OWN, so it holds at every endpoint. The Guard refuses\n\ a bearerless request before this runs, but the MCP server carries a typed op to\nits handler with no middleware in\ \ front of it \u2014 a handler that read no\nprincipal would answer such a caller with the whole registry. Reading\ \ the\nprincipal here is what makes the answer the same one over both." operationId: listOrganizations parameters: - in: header name: X-Forwarded-For required: false schema: type: string - in: query name: q required: false schema: type: string - in: query name: limit required: false schema: format: int64 type: integer - in: query name: cursor required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.ListOrganizationsOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the organizations you can act in, the ones you belong to first and the rest after, newest first, narrowed by an optional query against the name or the display name. tags: - iam x-app: iam x-public: true x-tool: true post: description: "Makes a new organization \u2014 the account your users, applications, roles,\nprojects and workspaces\ \ are all named inside. It is the first write in a new\ntenant, and a name already in use is refused rather than taken\ \ over." operationId: createOrganization requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.CreateOrganizationInput' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Organization' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Makes a new organization \u2014 the account your users, applications, roles, projects and workspaces are all\ \ named inside." tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/organizations/avatar: post: description: "Changes how an organization appears across Hanzo: the square mark\nbeside its name, as an uploaded image\ \ or as a single emoji. Sending an image\nclears the emoji and sending an emoji clears the image \u2014 an organization\ \ has\none mark, not a preference order \u2014 and sending neither clears both, which is\nhow it goes back to being\ \ drawn as its initial.\n\nAn image is an https link or the bytes inline as a data URL, up to 96 KiB.\nAnyone who\ \ administers the organization may set this; it is not reserved to\nthe platform.\n\nIt writes the two fields onto\ \ the stored row and touches nothing else, which\nupdate cannot do: update replaces the whole record, and a record\ \ read back\nfirst arrives masked, so a read-modify-write through it would persist the mask\nover the organization's\ \ own credential settings." operationId: setOrganizationAvatar requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.SetAvatarInput' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Organization' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Changes how an organization appears across Hanzo: the square mark beside its name, as an uploaded image or as a single emoji.' tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/organizations/profile: post: description: "Changes how an organization reads: its display name, its website\nand its favicon.\n\nIT EXISTS FOR THE\ \ REASON SetAvatar DOES, and the reason is worth stating\nbecause the obvious alternative is a trap. Update REPLACES\ \ the whole record,\nso a caller that wants to change one field has to send every other field\nback \u2014 and a record\ \ read back first arrives MASKED, so the read half of that\nread-modify-write hands you \"***\" for the master password\ \ and the salt, and\nthe write half stores it. Renaming an organization through Update therefore\ncosts it its credential\ \ settings; sending only the new name costs it\neverything else. Neither is a rename.\n\nSo this writes the fields\ \ it names and touches nothing else. A nil pointer is\nnot sent and not changed; an empty string is sent and clears\ \ the field." operationId: setOrganizationProfile requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.SetProfileInput' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Organization' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Changes how an organization reads: its display name, its website and its favicon.' tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/organizations/{owner}/{name}: delete: description: "Removes an organization and everything named inside it. There is no\nundo, and every session issued under\ \ it stops working.\n\nThe built-in admin organization cannot be deleted \u2014 losing it would leave the\naccount\ \ with no way back in." operationId: deleteOrganization parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.DeleteOrganizationOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes an organization and everything named inside it. tags: - iam x-app: iam x-public: true x-tool: true get: description: 'Returns one organization: its display, its defaults and the sign-in rules everyone in it inherits.' operationId: getOrganization parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Organization' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one organization: its display, its defaults and the sign-in rules everyone in it inherits.' tags: - iam x-app: iam x-public: true x-tool: true put: description: 'Changes an organization''s display, its defaults and the sign-in rules everyone in it inherits. Which organization it is does not change, and neither does when it was created.' operationId: updateOrganization parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.UpdateOrganizationInput' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Organization' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes an organization's display, its defaults and the sign-in rules everyone in it inherits. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/password: put: description: "Replaces the calling person's password. Only their own \u2014\nthere is no shape of this request that\ \ writes somebody else's.\n\nProve who you are with the password you are replacing, or \u2014 when you cannot\nsign\ \ in at all \u2014 with a code sent to the address the account already holds.\nExactly one of the two: a request carrying\ \ both proves nothing more than\neither, and answering it would mean deciding which one mattered.\n\nA reset also\ \ clears the account lockout, in the SAME transaction as the\ndigest. Replacing a credential retires the run of guesses\ \ against the old one,\nand without this a person who reset a forgotten password was still refused for\nup to fifteen\ \ more minutes \u2014 with the brand-new password they had just chosen." operationId: put_iam_password parameters: - in: header name: Cookie required: false schema: type: string - in: header name: Authorization required: false schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.passwordBody' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: ok '400': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: bad request default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Replaces the calling person's password. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/permissions: get: description: "Returns the permissions in one organization, newest first \u2014 each one a\ngrant saying which people\ \ or roles may do what, and to which resources." operationId: get_iam_permissions parameters: - in: query name: owner required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.ListResponse' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the permissions in one organization, newest first \u2014 each one a grant saying which people or roles\ \ may do what, and to which resources." tags: - iam x-app: iam x-public: true x-tool: true post: description: "Grants a permission \u2014 the call that gives a person or a role the ability to\ndo something. Adding\ \ refuses to overwrite a grant that already exists, so\nwidening an existing one is an update, never an accident." operationId: post_iam_permissions requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.Permission' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Permission' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Grants a permission \u2014 the call that gives a person or a role the ability to do something." tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/permissions/{owner}/{name}: delete: description: 'Revokes a permission. Everyone who held access only through it loses that access immediately; grants they hold by another route are untouched.' operationId: delete_iam_permissions_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.DeleteResponse' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Revokes a permission. tags: - iam x-app: iam x-public: true x-tool: true get: description: 'Returns one permission: who it grants to, what it allows, and the resources it covers.' operationId: get_iam_permissions_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Permission' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one permission: who it grants to, what it allows, and the resources it covers.' tags: - iam x-app: iam x-public: true x-tool: true put: description: 'Changes who a permission grants to, what it allows, or the resources it covers. Access changes as soon as the write lands. What the permission is called does not change, and neither does when it was created.' operationId: put_iam_permissions_by_owner_by_name parameters: - description: "Identity \u2014 the (owner, name) natural key." in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.Permission' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Permission' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes who a permission grants to, what it allows, or the resources it covers. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/preferences: post: description: "Saves the calling person's own settings and returns\nthe full set afterwards. Send only the settings you\ \ are changing \u2014 the rest\nare kept, so two screens can save at once without one undoing the other." operationId: post_iam_preferences summary: Saves the calling person's own settings and returns the full set afterwards. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/projects: get: description: "Returns your organization's projects, newest first \u2014 the scope\npeople pick between when their work\ \ is separated by product or client rather\nthan by team.\n\nYou see your own organization's projects and no one else's;\ \ which organization that\nis comes from your credentials, not from the request." operationId: get_iam_projects parameters: - in: query name: owner required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.projects.ListOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns your organization's projects, newest first \u2014 the scope people pick between when their work is\ \ separated by product or client rather than by team." tags: - iam x-app: iam x-public: true x-tool: true post: description: "Makes a project inside your organization \u2014 the scope people pick\nbetween when their work is separated\ \ by product or client rather than by team.\nA name already used in the organization is refused." operationId: post_iam_projects requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.projects.Input' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Project' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Makes a project inside your organization \u2014 the scope people pick between when their work is separated\ \ by product or client rather than by team." tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/projects/{owner}/{name}: delete: description: 'Removes a project. The people and roles in your organization are unchanged; what goes is the scope itself, so move anything addressed by it first.' operationId: delete_iam_projects_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.projects.DeleteOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a project. tags: - iam x-app: iam x-public: true x-tool: true get: description: 'Returns one project: what it is called and how it is set up.' operationId: get_iam_projects_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Project' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one project: what it is called and how it is set up.' tags: - iam x-app: iam x-public: true x-tool: true put: description: 'Changes a project''s settings. What it is called does not change, and neither does when it was created.' operationId: put_iam_projects_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.projects.Input' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Project' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes a project's settings. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/providers: get: description: "Returns your organization's providers, newest first \u2014 the\nidentity providers your people sign in\ \ with, and the senders and connectors\nyour applications go through.\n\nYou see your own organization's providers\ \ and no one else's; which organization\nthat is comes from your credentials, not from the request." operationId: listProviders parameters: - in: query name: owner required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.listProvidersOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns your organization's providers, newest first \u2014 the identity providers your people sign in with,\ \ and the senders and connectors your applications go through." tags: - iam x-app: iam x-public: true x-tool: true post: description: "Adds an identity provider your people can sign in with, or a\nservice your applications send through \u2014\ \ a social or enterprise login, an email\nor SMS sender, a storage or payment connector.\n\nA provider is configured\ \ once and then switched on per application, so several\napplications can share one set of credentials." operationId: addProvider requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.Provider' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.providerResult' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Adds an identity provider your people can sign in with, or a service your applications send through \u2014\ \ a social or enterprise login, an email or SMS sender, a storage or payment connector." tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/providers/{owner}/{name}: delete: description: 'Removes a provider. Sign-in through it stops for every application that used it, so give those applications another method first. A provider that is already gone answers "nothing changed" rather than an error, so the call is safe to repeat.' operationId: deleteProvider parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.mutationResult' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a provider. tags: - iam x-app: iam x-public: true x-tool: true get: description: 'Returns one provider: what it connects to and how it is configured. Its credentials come back masked.' operationId: getProvider parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.providerResult' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one provider: what it connects to and how it is configured.' tags: - iam x-app: iam x-public: true x-tool: true put: description: "Changes a provider's settings or rotates the credentials it\nholds. The change takes effect on the next\ \ sign-in through it \u2014 sessions\nalready issued are unaffected.\n\nA provider that is not there answers \"nothing\ \ changed\" rather than an error, so\nthe call is safe to repeat." operationId: updateProvider parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.Provider' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.mutationResult' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes a provider's settings or rotates the credentials it holds. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/registry/jwks: get: description: "Publishes the public key your registry uses to verify the tokens issued\nabove \u2014 the one URL to configure\ \ so the registry trusts logins without holding\nany secret of its own.\n\nIf no signing key is available it refuses\ \ rather than publishing an empty set,\nbecause a registry that trusts nothing looks identical to one that trusts\n\ everything until somebody tries to push." operationId: get_iam_registry_jwks summary: "Publishes the public key your registry uses to verify the tokens issued above \u2014 the one URL to configure\ \ so the registry trusts logins without holding any secret of its own." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/registry/token: get: description: 'Signs a container client in to your registry. `docker login`, and every build tool that pushes or pulls images, lands here: it exchanges the credential for a short-lived token scoped to exactly the repositories that credential may touch. Both of the shapes container tooling uses are accepted, so the same login works whichever client your pipeline runs.' operationId: get_iam_registry_token summary: Signs a container client in to your registry. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). post: description: 'Signs a container client in to your registry. `docker login`, and every build tool that pushes or pulls images, lands here: it exchanges the credential for a short-lived token scoped to exactly the repositories that credential may touch. Both of the shapes container tooling uses are accepted, so the same login works whichever client your pipeline runs.' operationId: post_iam_registry_token summary: Signs a container client in to your registry. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/release: post: description: 'Steps a platform operator back out: it returns their own access token with no organization assumed, which is the credential they had before they stepped in. Recorded like the step in.' operationId: post_iam_release parameters: - in: header name: Authorization required: false schema: type: string - in: header name: X-Forwarded-For required: false schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.assumeBody' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: ok '400': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: bad request '401': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: unauthorized default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Steps a platform operator back out: it returns their own access token with no organization assumed, which is the credential they had before they stepped in.' tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/roles: get: description: "Returns your organization's roles, newest first \u2014 each a named group of\npeople that permissions\ \ are granted to.\n\nYou see your own organization's roles and no one else's; which organization\nthat is comes from\ \ your credentials, not from the request." operationId: get_iam_roles parameters: - in: query name: owner required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.roles.ListOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns your organization's roles, newest first \u2014 each a named group of people that permissions are granted\ \ to." tags: - iam x-app: iam x-public: true x-tool: true post: description: "Makes a role \u2014 a named group of people that permissions are granted to.\nGranting to a role rather\ \ than to each person is what keeps access correct as\nyour team changes: add someone to the role and they inherit\ \ everything it can\ndo. A name already used in your organization is refused." operationId: post_iam_roles requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.Input' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Role' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Makes a role \u2014 a named group of people that permissions are granted to." tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/roles/{owner}/{name}: delete: description: 'Removes a role. Everyone in it loses the access it carried; their accounts, and any other role they hold, are untouched.' operationId: delete_iam_roles_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.roles.DeleteOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a role. tags: - iam x-app: iam x-public: true x-tool: true get: description: 'Returns one role: who is in it, and the roles it includes.' operationId: get_iam_roles_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Role' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one role: who is in it, and the roles it includes.' tags: - iam x-app: iam x-public: true x-tool: true put: description: 'Changes who is in a role, or which roles it includes. Access changes for everyone in it as soon as the write lands. What the role is called does not change, and neither does when it was created.' operationId: put_iam_roles_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - description: 'Name addresses the team on update and names it on create; every other field is content and binds from the BODY, never the URL.' in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.Input' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Role' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes who is in a role, or which roles it includes. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/scim/v2/ResourceTypes: get: description: 'Returns the kinds of record this directory provisions and the address of each, so your identity provider discovers them rather than having them configured by hand.' operationId: get_iam_scim_v2_resourcetypes responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.listResponse' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the kinds of record this directory provisions and the address of each, so your identity provider discovers them rather than having them configured by hand. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/scim/v2/ResourceTypes/{name}: get: description: Returns one provisionable record kind in full. operationId: get_iam_scim_v2_resourcetypes_by_name parameters: - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: {} description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '404': content: application/json: schema: {} description: not found default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one provisionable record kind in full. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/scim/v2/Schemas: get: description: 'Returns the attribute definitions this directory understands, so your identity provider knows which fields it may send and what they mean before it sends any.' operationId: get_iam_scim_v2_schemas responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.listResponse' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the attribute definitions this directory understands, so your identity provider knows which fields it may send and what they mean before it sends any. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/scim/v2/Schemas/{id}: get: description: Returns one attribute definition in full. operationId: get_iam_scim_v2_schemas_by_id parameters: - in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: {} description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '404': content: application/json: schema: {} description: not found default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one attribute definition in full. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/scim/v2/ServiceProviderConfig: get: description: "Tells your identity provider which parts of SCIM this\ndirectory supports, so it configures itself instead\ \ of you filling in a form.\n\nFiltering and partial updates are supported. Bulk operations, sorting and\nentity tags\ \ are not \u2014 an IdP that reads this will not attempt them." operationId: get_iam_scim_v2_serviceproviderconfig responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.config' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Tells your identity provider which parts of SCIM this directory supports, so it configures itself instead of you filling in a form. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/scim/v2/Users: get: description: 'Returns the people in your organization to your identity provider, in the standard SCIM shape, so an IdP can reconcile its directory against ours. Searchable by username or email address, and paged. Reading the whole list takes an administrator; an ordinary person is refused.' operationId: get_iam_scim_v2_users summary: Returns the people in your organization to your identity provider, in the standard SCIM shape, so an IdP can reconcile its directory against ours. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). post: description: "Provisions a person from your identity provider \u2014 how a new hire\ngets an account here automatically\ \ when they are added over there.\n\nTakes an administrator. Making someone an administrator takes more than that,\n\ so an IdP integration cannot escalate anyone by setting a flag." operationId: post_iam_scim_v2_users summary: "Provisions a person from your identity provider \u2014 how a new hire gets an account here automatically when\ \ they are added over there." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/scim/v2/Users/{owner}/{name}: delete: description: "Deprovisions a person \u2014 how removing someone in your identity\nprovider removes their access here.\ \ Their sessions stop working immediately.\nTakes an administrator." operationId: delete_iam_scim_v2_users_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string summary: "Deprovisions a person \u2014 how removing someone in your identity provider removes their access here." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). get: description: 'Returns one person in the standard SCIM shape. An administrator may read anyone in the organization; everyone else may read only themselves.' operationId: get_iam_scim_v2_users_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string summary: Returns one person in the standard SCIM shape. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). patch: description: "Applies a partial change from your identity provider \u2014 one attribute\nmoved, not the whole record\ \ resent.\n\nThe change is applied onto the person as they currently are, so everything you\ndid not mention keeps\ \ its value, including the parts SCIM does not describe." operationId: patch_iam_scim_v2_users_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string summary: "Applies a partial change from your identity provider \u2014 one attribute moved, not the whole record resent." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). put: description: "Overwrites a person's SCIM attributes with what your identity\nprovider sends \u2014 how a change made\ \ there lands here.\n\nOnly the attributes SCIM describes are replaced. Anything the standard does not\ncover \u2014\ \ their multi-factor enrolment above all \u2014 survives untouched, so a\nroutine sync from your IdP can never quietly\ \ strip someone's second factor or\nbring a deleted account back." operationId: put_iam_scim_v2_users_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string summary: "Overwrites a person's SCIM attributes with what your identity provider sends \u2014 how a change made there\ \ lands here." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/service-accounts: get: description: "Returns your organization's service accounts \u2014 what each is called and\nwhen it was created. Never\ \ their secrets: a key's secret half exists in a\nresponse exactly once, when it is minted. Paginated in memory over\ \ the already org-scoped\nslice \u2014 the set per org is small, so a dedicated count query is overkill\n(v1 service_account.go:296-307)." operationId: get_iam_service-accounts parameters: - description: Organization is the organization whose service accounts to list. Required. in: query name: organization required: false schema: type: string - description: "P is the 1-indexed page to return. Paging takes both p and pageSize \u2014\nleave either out, or send\ \ something that is not a number, and the whole\nlist comes back." in: query name: p required: false schema: format: int64 type: integer - description: Size is how many accounts a page holds. in: query name: pageSize required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '400': content: application/json: schema: $ref: '#/components/schemas/iam.Answer' description: bad request default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns your organization's service accounts \u2014 what each is called and when it was created." tags: - iam x-app: iam x-public: true x-tool: true post: description: "Makes a service account \u2014 an identity for a program rather than a\nperson, for a script, a bot or\ \ a deployment that has to authenticate on its\nown.\n\nIt comes back with its first key, and the secret half is shown\ \ ONCE, here.\nThere is no way to read it again; if you lose it, rotate." operationId: post_iam_service-accounts summary: "Makes a service account \u2014 an identity for a program rather than a person, for a script, a bot or a deployment\ \ that has to authenticate on its own." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/service-accounts/{name}: delete: description: Serves DELETE /v1/iam/service-accounts/:name. operationId: delete_iam_service-accounts_by_name parameters: - in: path name: name required: true schema: type: string summary: Serves DELETE /v1/iam/service-accounts/:name. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/service-accounts/{name}/keys: post: description: 'Serves POST /v1/iam/service-accounts/:name/keys: mint a fresh key, invalidating the prior one, and return the new raw secret exactly once.' operationId: post_iam_service-accounts_by_name_keys parameters: - in: path name: name required: true schema: type: string summary: 'Serves POST /v1/iam/service-accounts/:name/keys: mint a fresh key, invalidating the prior one, and return the new raw secret exactly once.' tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/sessions: get: description: 'Returns who is currently signed in to an organization, newest first, and can be narrowed to one person or one application. It is what you read before signing someone out. Which organization comes from your credentials, not from the request: you read your own and no one else''s. A session row names a live account and the applications it is signed in to, so the tenant is decided here rather than taken from the query.' operationId: listSessions parameters: - in: query name: owner required: false schema: type: string - in: query name: name required: false schema: type: string - in: query name: application required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.ListSessionsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns who is currently signed in to an organization, newest first, and can be narrowed to one person or one application. tags: - iam x-app: iam x-public: true x-tool: true post: description: 'Records a sign-in and answers with the cookie id it minted. Signing in again from another browser adds to the session rather than replacing it, so one person can be signed in from a laptop and a phone at once. Ask for an exclusive sign-in and the opposite holds: the new sign-in is the only one left and every other browser is signed out. That is the setting to use when one person may hold only one live session at a time.' operationId: createSession requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.CreateSessionIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Session' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records a sign-in and answers with the cookie id it minted. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/sessions/{owner}/{name}/{application}: delete: description: "Signs a person out of one application \u2014 the session ends and every\nbrowser carrying it stops being\ \ authenticated.\n\nA session that is already gone reports that nothing was deleted rather than an\nerror, so the\ \ call is safe to repeat." operationId: deleteSession parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string - in: path name: application required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.DeleteSessionOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Signs a person out of one application \u2014 the session ends and every browser carrying it stops being authenticated." tags: - iam x-app: iam x-public: true x-tool: true get: description: "Returns one person's session in one application \u2014 when it began and which\nbrowsers or devices are\ \ still carrying it." operationId: getSession parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string - in: path name: application required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Session' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one person's session in one application \u2014 when it began and which browsers or devices are still\ \ carrying it." tags: - iam x-app: iam x-public: true x-tool: true put: description: "Names the browsers a session keeps \u2014 signing out the ones you leave off\nwhile the session itself\ \ stays live. A session that does not exist is reported\nas missing rather than created." operationId: updateSession parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string - in: path name: application required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.UpdateSessionIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Session' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Names the browsers a session keeps \u2014 signing out the ones you leave off while the session itself stays\ \ live." tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/signin: post: description: 'Completes a sign-in: it exchanges the one-time code your application was handed at the end of the login flow for a live session, and returns the signed-in account. The code works once. This is the call that turns a finished login into something your application can act on.' operationId: post_iam_signin summary: 'Completes a sign-in: it exchanges the one-time code your application was handed at the end of the login flow for a live session, and returns the signed-in account.' tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/signup: post: description: "Creates an account from the sign-up form and applies the\napplication's own sign-up rules \u2014 whether\ \ self-service registration is open at\nall, and which fields it requires.\n\nThe password is hashed before it is\ \ stored and is never returned." operationId: post_iam_signup summary: "Creates an account from the sign-up form and applies the application's own sign-up rules \u2014 whether self-service\ \ registration is open at all, and which fields it requires." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/teams: get: description: "Returns your organization's teams, newest first \u2014 each a named set of\npeople that roles and permissions\ \ are granted to.\n\nYou see your own organization's teams and no one else's; which organization\nthat is comes from\ \ your credentials, not from the request." operationId: get_iam_teams responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.teams.ListOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns your organization's teams, newest first \u2014 each a named set of people that roles and permissions\ \ are granted to." tags: - iam x-app: iam x-public: true x-tool: true post: description: "Makes a team \u2014 a named set of people that roles and permissions grant\nto. Granting to a team rather\ \ than to each person keeps access correct as\npeople come and go: add someone and they inherit what the team can\ \ do. A name\nalready used in your organization is refused." operationId: post_iam_teams requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.teams.Input' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Team' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Makes a team \u2014 a named set of people that roles and permissions grant to." tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/teams/{name}: delete: description: 'Removes a team. Everyone in it loses the access it carried; their accounts, and any other team they are in, are untouched.' operationId: delete_iam_teams_by_name parameters: - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.teams.DeleteOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a team. tags: - iam x-app: iam x-public: true x-tool: true get: description: 'Returns one team: who is in it.' operationId: get_iam_teams_by_name parameters: - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Team' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one team: who is in it.' tags: - iam x-app: iam x-public: true x-tool: true put: description: 'Changes who is in a team. Access changes for everyone in it as soon as the write lands. The name and the created stamp do not change.' operationId: put_iam_teams_by_name parameters: - description: 'Name addresses the team on update and names it on create; every other field is content and binds from the BODY, never the URL.' in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.teams.Input' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Team' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes who is in a team. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/tokens: get: description: 'Returns the access tokens issued in your organization, newest first, and can be narrowed to one organization. Use it to see what is currently authorized before revoking anything.' operationId: listTokens parameters: - in: query name: owner required: false schema: type: string - in: query name: organization required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.listTokensOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the access tokens issued in your organization, newest first, and can be narrowed to one organization. tags: - iam x-app: iam x-public: true x-tool: true post: description: "Records an access token \u2014 the credential an application or integration\npresents on a caller's behalf." operationId: addToken requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.Token' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.tokenResult' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Records an access token \u2014 the credential an application or integration presents on a caller's behalf." tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/tokens/issue: post: description: "Mints an access token for the `?id=/` target\nuser (optional `?aud=` resource, RFC 8707),\ \ issued by the authenticated +\nallow-listed confidential client. The token's subject + owner are the TARGET\nUSER's,\ \ so a resource server scopes on the validated owner claim to the user's\ntenant \u2014 indistinguishable from a token\ \ the user obtained directly. Response is\nthe camelCase `{accessToken, expiresIn}` body identity.ts consumes. Equivalent\ \ to\nthe RFC 8693 token-exchange grant, minus the subject_token proof (the console has\nthe user's id, not a token)\ \ \u2014 the reason this compat shim exists." operationId: post_iam_tokens_issue summary: Mints an access token for the `?id=/` target user (optional `?aud=` resource, RFC 8707), issued by the authenticated + allow-listed confidential client. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/tokens/{owner}/{name}: delete: description: 'Revokes an access token. Whatever was using it stops being authorized at once. A token that is already gone answers "nothing changed" rather than an error, so the call is safe to repeat.' operationId: deleteToken parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.tokenMutation' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Revokes an access token. tags: - iam x-app: iam x-public: true x-tool: true get: description: 'Returns one access token: who and what it was issued to, and when it expires.' operationId: getToken parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.tokenResult' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one access token: who and what it was issued to, and when it expires.' tags: - iam x-app: iam x-public: true x-tool: true put: description: 'Changes an access token''s scope or expiry. A token that is not there answers "nothing changed" rather than an error, so the call is safe to repeat.' operationId: updateToken parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.Token' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.tokenMutation' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes an access token's scope or expiry. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/unlink: post: description: "Disconnects one sign-in identity from an account, so that provider can\nno longer be used to sign in as\ \ that person. Their account and every other way\nthey sign in are untouched. Two principals may do it, and\nonly\ \ two: the account holder itself, and a SuperAdmin (a member of the reserved\nadmin org, the one predicate). An ORG\ \ ADMIN deliberately may NOT \u2014 unlinking is\nnot tenant administration, it is unpicking someone's own sign-in\ \ method, so the\ngeneric org-admin rule is the wrong answer here.\n\nA holder unlinking itself must also be permitted\ \ by the application \u2014 the\nprovider link's CanUnlink flag \u2014 so an organization that mandates federated\n\ sign-in cannot have its users strand themselves. A SuperAdmin is not bound by\nthat flag; it is the platform's own\ \ recovery path. Fail-closed throughout." operationId: post_iam_unlink summary: Disconnects one sign-in identity from an account, so that provider can no longer be used to sign in as that person. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/users: get: description: "Returns a page of the people in an organization, with the total so you\ncan page through the rest. Passwords,\ \ API secrets and MFA material are stripped\nfrom every entry.\n\nWhich organization comes from your credentials,\ \ not from the request: you read\nyour own and no one else's, and a credential whose scope spans tenants reads\nthe\ \ tenant it names \u2014 or, naming none, every one of them." operationId: get_iam_users parameters: - in: query name: owner required: false schema: type: string - description: "Email narrows the page to the accounts carrying one address. Looking a\nperson up by their address is\ \ a QUERY over the collection, not an item\nread: an address is not the natural key, two rows in one org can carry\n\ one, and a caller that gets a page SEES both \u2014 where a single-item read\nwould have to choose, and choosing\ \ is how somebody joins a team under a\ncolleague's identity." in: query name: email required: false schema: type: string - in: query name: limit required: false schema: format: int64 type: integer - in: query name: offset required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.ListOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns a page of the people in an organization, with the total so you can page through the rest. tags: - iam x-app: iam x-public: true x-tool: true post: description: "Adds a person to your organization. Send a password and it becomes the\none they sign in with; it is hashed\ \ before it is stored and never comes back\nin any response.\n\nThe username is checked against the same rule every\ \ account in the Hanzo Cloud\nis held to, whichever way it was created \u2014 this call, password signup, a social\n\ sign-in, or SCIM \u2014 so a name accepted here works everywhere.\n\nA name already taken in your organization is\ \ refused rather than overwritten." operationId: post_iam_users requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.CreateInput' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.User' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Adds a person to your organization. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/users/{owner}/{name}: delete: description: "Removes a person from your organization. Their sessions stop working\nimmediately and the account is gone\ \ rather than suspended \u2014 to keep the record\nand only stop sign-in, update the user instead.\n\nA SuperAdmin's\ \ account is removed only by a SuperAdmin." operationId: delete_iam_users_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.DeleteOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a person from your organization. tags: - iam x-app: iam x-public: true x-tool: true get: description: 'Returns one person in your organization, addressed by their username or by their email address. Passwords, API secrets and MFA material are stripped from the response. An address that names two accounts names none: the read refuses rather than picking one, and says so instead of reporting "no such user". Handing back an arbitrary one of two rows is how somebody gets added to a team under a colleague''s identity.' operationId: get_iam_users_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string - in: query name: email required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.User' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one person in your organization, addressed by their username or by their email address. tags: - iam x-app: iam x-public: true x-tool: true put: description: 'Changes a person''s profile, their roles, or the credentials they sign in with. Send a password to reset it; leave it out and their current one keeps working. Who they are does not change: their organization, username and the identifier their existing sessions are keyed on all survive the write, so an update never signs anyone out. A SuperAdmin''s account is changed only by a SuperAdmin.' operationId: put_iam_users_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.UpdateInput' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.User' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes a person's profile, their roles, or the credentials they sign in with. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/users/{owner}/{name}/keys: delete: description: 'Clears the target user''s key of the requested TYPE (immediate revoke). Scoped by the same `?type` field mint takes, so revoking the browser key leaves the server key working. A secret key''s stored value is the sk- in its schema.Key row.' operationId: delete_iam_users_by_owner_by_name_keys parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string summary: Clears the target user's key of the requested TYPE (immediate revoke). tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). post: description: '(re)generates the target user''s key of the requested TYPE and returns it once, over the shared authorizeMinter + mintTarget seam. `?type=secret` (the default) yields the confidential sk-; `?type=publishable` yields the pk- that is safe to ship in client JS and resolves to an org, never a principal. It writes the schema.Key row that the resolvers actually read. schema.User.AccessKey is not a credential and nothing resolves it, so a key stamped there would authenticate nobody.' operationId: post_iam_users_by_owner_by_name_keys parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string summary: (re)generates the target user's key of the requested TYPE and returns it once, over the shared authorizeMinter + mintTarget seam. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/verification-codes: post: description: "Validates the request and asks otp to get a code to the\nperson. The request fields are read via fiber's\ \ FormValue \u2014 the escape hatch zip\nexposes for form bodies (multipart or urlencoded) \u2014 since the typed\ \ JSON Bind does\nnot apply here. v1 also accepts countryCode/method/checkUser/captchaType; iam\nignores them (the\ \ captcha/forget/MFA flows those drive are not ported), and\nCAPTCHA verification is likewise not enforced \u2014\ \ iam models no captcha provider \u2014\nso the code is issued once the destination and application validate." operationId: post_iam_verification-codes summary: Validates the request and asks otp to get a code to the person. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/web3/nonce: get: description: 'Starts a wallet sign-in: it returns a one-time challenge for the wallet to sign. The challenge is good once and is tied to the site that asked for it, so a signature collected elsewhere cannot be replayed here.' operationId: get_iam_web3_nonce summary: 'Starts a wallet sign-in: it returns a one-time challenge for the wallet to sign.' tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/web3/verify: post: description: "Completes a wallet sign-in: it verifies the signed challenge and, if it\nholds, signs the wallet's owner\ \ in.\n\nThis IS the login \u2014 it answers exactly as a password sign-in does, so the rest\nof your flow does not\ \ branch on how somebody arrived." operationId: post_iam_web3_verify summary: 'Completes a wallet sign-in: it verifies the signed challenge and, if it holds, signs the wallet''s owner in.' tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/webauthn-credentials: get: description: "Returns the passkeys and security keys registered to\none person, newest first \u2014 which device each\ \ lives on and when it was\nregistered.\n\nYours by default. Name somebody else and you get them only if you already\n\ administer their account, which is the same authority that governs reading\ntheir user record \u2014 so this list\ \ can never show more people than the surface\nbeside it already does.\n\nThere is no organization-wide list, by design.\ \ Scoping to the ORG would hand an\norg admin every member's credential rows in one answer and a SuperAdmin every\n\ tenant's, while a plain member could not read even their own (an unnamed target\nfails the Guard's tenant rule). One\ \ scope answers both halves cleanly: the\nanswer is a person's, and the caller is that person unless they say otherwise\n\ and may." operationId: listWebauthnCredentials parameters: - in: query name: user required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.listWebauthnCredentialsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the passkeys and security keys registered to one person, newest first \u2014 which device each lives\ \ on and when it was registered." tags: - iam x-app: iam x-public: true x-tool: true post: description: 'Registers a passkey or security key for a person, so they can sign in with their device instead of a password.' operationId: addWebauthnCredential requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.WebauthnCredential' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.webauthnCredentialResult' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Registers a passkey or security key for a person, so they can sign in with their device instead of a password. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/webauthn-credentials/{owner}/{name}: delete: description: "Removes a passkey or security key \u2014 what you call when\na device is lost. Make sure the person has\ \ another way to sign in first.\n\nA credential that is already gone answers \"nothing changed\" rather than an\n\ error, so the call is safe to repeat." operationId: deleteWebauthnCredential parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.webauthnCredentialMutationResult' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Removes a passkey or security key \u2014 what you call when a device is lost." tags: - iam x-app: iam x-public: true x-tool: true get: description: 'Returns one passkey or security key: whose it is, what device it lives on, and when it was registered.' operationId: getWebauthnCredential parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.webauthnCredentialResult' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one passkey or security key: whose it is, what device it lives on, and when it was registered.' tags: - iam x-app: iam x-public: true x-tool: true put: description: 'Renames a registered passkey or security key, so a person can tell their devices apart. A credential that is not there answers "nothing changed" rather than an error, so the call is safe to repeat.' operationId: updateWebauthnCredential parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.WebauthnCredential' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.webauthnCredentialMutationResult' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Renames a registered passkey or security key, so a person can tell their devices apart. tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/webauthn/signin/begin: get: description: "Starts a passkey sign-in: it returns the challenge the person's\nauthenticator signs.\n\nThe account is\ \ named in the query, and the challenge is bound to it, so what may\nanswer is decided here \u2014 by the server,\ \ from the row \u2014 and the finish checks the\nanswer against that decision rather than recomputing it." operationId: get_iam_webauthn_signin_begin summary: 'Starts a passkey sign-in: it returns the challenge the person''s authenticator signs.' tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/webauthn/signin/finish: post: description: "Verifies the signed challenge and signs the person in.\n\nIt answers exactly as a password sign-in does\ \ \u2014 the same envelope, through the\nsame grant \u2014 so nothing downstream branches on how somebody arrived." operationId: post_iam_webauthn_signin_finish summary: Verifies the signed challenge and signs the person in. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/webauthn/signup/begin: get: description: 'Starts enrolling a passkey for the signed-in person: it returns the options their browser hands to the authenticator. Passkeys already on the account are EXCLUDED, so a second enrollment on a device that already holds one is refused by the authenticator itself rather than silently producing a duplicate the person cannot tell apart.' operationId: get_iam_webauthn_signup_begin summary: 'Starts enrolling a passkey for the signed-in person: it returns the options their browser hands to the authenticator.' tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/webauthn/signup/finish: post: description: 'Verifies the newly created passkey and stores it, so the person can sign in with their device from then on.' operationId: post_iam_webauthn_signup_finish summary: Verifies the newly created passkey and stores it, so the person can sign in with their device from then on. tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/whoami: get: description: "Tells you who the current caller is \u2014 the lightweight check a\npage makes on load to decide whether\ \ to render signed-in or signed-out.\n\nIt answers for a session cookie or a bearer token alike, and says plainly\ \ when\nnobody is signed in rather than failing." operationId: get_iam_whoami summary: "Tells you who the current caller is \u2014 the lightweight check a page makes on load to decide whether to\ \ render signed-in or signed-out." tags: - iam x-app: iam x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/iam/workspaces: get: description: "Returns your organization's workspaces, newest first \u2014 the scope a\nteam works in, alongside projects\ \ rather than instead of them.\n\nYou see your own organization's workspaces and no one else's; which organization\ \ that\nis comes from your credentials, not from the request." operationId: get_iam_workspaces parameters: - in: query name: owner required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.workspaces.ListOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns your organization's workspaces, newest first \u2014 the scope a team works in, alongside projects\ \ rather than instead of them." tags: - iam x-app: iam x-public: true x-tool: true post: description: "Makes a workspace inside your organization \u2014 the scope a team works in,\nalongside projects rather\ \ than instead of them. A name already used in the\norganization is refused." operationId: post_iam_workspaces requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.workspaces.Input' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Workspace' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Makes a workspace inside your organization \u2014 the scope a team works in, alongside projects rather than\ \ instead of them." tags: - iam x-app: iam x-public: true x-tool: true /v1/iam/workspaces/{owner}/{name}: delete: description: 'Removes a workspace. The people and roles in your organization are unchanged; what goes is the scope itself.' operationId: delete_iam_workspaces_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.workspaces.DeleteOutput' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a workspace. tags: - iam x-app: iam x-public: true x-tool: true get: description: 'Returns one workspace: what it is called and how it is set up.' operationId: get_iam_workspaces_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Workspace' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one workspace: what it is called and how it is set up.' tags: - iam x-app: iam x-public: true x-tool: true put: description: 'Changes a workspace''s settings. What it is called does not change, and neither does when it was created.' operationId: put_iam_workspaces_by_owner_by_name parameters: - in: path name: owner required: true schema: type: string - in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/iam.workspaces.Input' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/iam.Workspace' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes a workspace's settings. tags: - iam x-app: iam x-public: true x-tool: true /v1/images/generations: post: description: "Implements POST /v1/images/generations (OpenAI-compatible).\n\nBody: {\"model\": \"...\", \"prompt\":\ \ \"...\", \"n\"?: int, \"size\"?: \"1024x1024\",\n\n\t\"response_format\"?: \"url\"|\"b64_json\"}\n\nIt authenticates\ \ the caller, resolves the model to its upstream provider via\nthe shared routing table (zen3-image* \u2192 do-ai\ \ fal diffusion), reserves the\nper-image budget, generates the image(s) through the do-ai async image\nclient, records\ \ usage for billing, and returns the OpenAI images response." operationId: post_images_generations responses: '200': content: application/json: schema: $ref: '#/components/schemas/openai.ImageResponse' description: Success. '401': description: No credential, or one this service does not accept. '403': description: A valid credential that may not do this. summary: Implements POST /v1/images/generations (OpenAI-compatible). tags: - ai x-app: ai x-public: true /v1/index/health: get: description: "Reports whether the search plane can serve.\n\nAnswers the dialect's `{\"status\":\"available\"}` when\ \ the index store is\nreadable. It FAILS CLOSED \u2014 an unreadable store answers 503 with\n`{\"status\":\"unavailable\"\ }` rather than an empty result set, because a\nMeilisearch client probes this before it will use a server at all and\ \ a\ncheerful 200 over a broken volume turns \"search is down\" into \"nothing\nmatched\". It requires no principal\ \ and reads no tenant data." operationId: get_index_health responses: '200': content: application/json: schema: $ref: '#/components/schemas/index.indexHealth' description: ok '503': content: application/json: schema: $ref: '#/components/schemas/index.indexHealth' description: service unavailable default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports whether the search plane can serve. tags: - index x-app: index x-public: true x-tool: true /v1/index/indexes: get: description: "Lists the indexes your org holds.\n\nAnswers every index in the caller's own org with its primary key\ \ and\ntimestamps. Without it an index whose uid a caller has forgotten is\nunreachable \u2014 there is no other way\ \ to enumerate what an org holds. The page\nis the whole set: an org's index count is small by construction, so `limit`\n\ and `total` both report it.\n\nThe tenant is the org minted from the VALIDATED bearer's owner claim, never a\nclient-supplied\ \ header, and two orgs may both hold an index named \"messages\"\nwithout either seeing the other. Without a validated\ \ principal the answer is\n403 carrying the dialect's `invalid_api_key` body." operationId: get_index_indexes responses: '200': content: application/json: schema: $ref: '#/components/schemas/index.indexList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the indexes your org holds. tags: - index x-app: index x-public: true x-tool: true post: description: "Creates an index.\n\nRegisters a named index in the caller's own org and answers the dialect's\nEnqueuedTask.\ \ It is idempotent: creating an index that already exists returns\nthe same receipt and changes nothing, which is\ \ what lets a client create on\nstartup without checking first.\n\n`primaryKey` is optional \u2014 the first write\ \ establishes one when it is omitted.\nAn index is a ROW here rather than a table, so an unusual uid is stored\nverbatim\ \ instead of being sanitised into a schema name.\n\nThe 202 and its `enqueued` task are DIALECT COMPATIBILITY, not\ \ a promise of\nlater work: the write is already applied when this answers. A client that\npolls waitForTask resolves\ \ immediately." operationId: post_index_indexes requestBody: content: application/json: example: primaryKey: id uid: messages schema: $ref: '#/components/schemas/index.indexNew' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/index.indexEnqueued' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates an index. tags: - index x-app: index x-public: true x-tool: true /v1/index/indexes/{uid}: delete: description: 'Deletes an index and everything in it. Drops the index and every document in it from the caller''s own org, and answers the dialect''s EnqueuedTask. This is the only way to retire an index; without it a mistaken uid is permanent. Deleting an index that is not there succeeds, so a cleanup pass is safe to re-run. The 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of later work: the documents are already gone when this answers.' operationId: delete_index_indexes_by_uid parameters: - in: path name: uid required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/index.indexEnqueued' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes an index and everything in it. tags: - index x-app: index x-public: true x-tool: true get: description: "Reads one index's definition.\n\nAnswers the index's uid, primary key and timestamps. An index this org\ \ does\nnot hold answers 404 carrying the dialect's `index_not_found` \u2014 the code a\nMeilisearch client reads\ \ as permission to create it, which is why this is a\nrefusal rather than an empty object.\n\nThe uid is scoped to\ \ the caller's own org, so another tenant's index is\nindistinguishable from one that never existed: this surface\ \ is not an\nexistence oracle." operationId: get_index_indexes_by_uid parameters: - in: path name: uid required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/index.indexView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads one index's definition. tags: - index x-app: index x-public: true x-tool: true /v1/index/indexes/{uid}/documents: get: description: "Pages through the documents in an index.\n\nAnswers the org's stored documents in insertion order, whole,\ \ with the page's\nbounds and the index's total. It is the enumeration surface \u2014 search ranks by\nrelevance and\ \ cannot walk a corpus \u2014 so a caller reconciling what it has\nwritten reads it here.\n\nAn index this org does\ \ not hold answers 404 carrying the dialect's\n`index_not_found`." operationId: get_index_indexes_by_uid_documents parameters: - in: path name: uid required: true schema: type: string - in: query name: limit required: false schema: type: string - in: query name: offset required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/index.indexDocuments' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Pages through the documents in an index. tags: - index x-app: index x-public: true x-tool: true post: description: "Writes documents into the caller's own index, keyed by the index's primary key: a document whose key is\ \ already present is REPLACED whole. The body is the dialect's own \u2014 an array of documents, or a single document\ \ \u2014 and each is stored verbatim, so a read gives back exactly what was written.\n\nThe index is CREATED when\ \ it is missing rather than refused, because a Meilisearch client writes before it configures.\n\nThe tenant is the\ \ org minted from the VALIDATED bearer's owner claim, never a client-supplied header, so two orgs may both hold an\ \ index named \"messages\" and neither can see the other's documents. Without a validated principal the answer is\ \ 403 carrying the dialect's `invalid_api_key` body.\n\nThe 202 and its `enqueued` task are DIALECT COMPATIBILITY,\ \ not a promise of later work: the documents are searchable when this answers, and a client that polls waitForTask\ \ resolves immediately." operationId: post_index_indexes_by_uid_documents parameters: - in: path name: uid required: true schema: type: string requestBody: content: application/json: schema: oneOf: - items: {} type: array - {} responses: 2XX: content: application/json: schema: $ref: '#/components/schemas/indexEnqueued' description: Success summary: Add or replace documents in an index tags: - index x-app: index x-public: true put: description: 'The dialect''s update spelling of the write above, and the same act: an upsert keyed by the index''s primary key. The JS client''s addDocuments and updateDocuments both reduce to this for whole documents, so both spellings are served and both behave identically. The tenant is the org minted from the VALIDATED bearer''s owner claim, never a client-supplied header. Without a validated principal the answer is 403 carrying the dialect''s `invalid_api_key` body. The 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of later work: the write is already applied when this answers.' operationId: put_index_indexes_by_uid_documents parameters: - in: path name: uid required: true schema: type: string requestBody: content: application/json: schema: oneOf: - items: {} type: array - {} responses: 2XX: content: application/json: schema: $ref: '#/components/schemas/indexEnqueued' description: Success summary: Add or update documents in an index tags: - index x-app: index x-public: true /v1/index/indexes/{uid}/documents/delete-batch: post: description: 'Removes every named document from the caller''s own index. The body is the dialect''s own: a bare array of primary keys, which may be strings or numbers. A key that is not there is not an error, so a client reconciling its own corpus can send one list rather than checking each key first. The tenant is the org minted from the VALIDATED bearer''s owner claim, never a client-supplied header. Without a validated principal the answer is 403 carrying the dialect''s `invalid_api_key` body. The 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of later work: the documents are already gone when this answers.' operationId: post_index_indexes_by_uid_documents_delete-batch parameters: - in: path name: uid required: true schema: type: string requestBody: content: application/json: schema: items: {} type: array responses: 2XX: content: application/json: schema: $ref: '#/components/schemas/indexEnqueued' description: Success summary: Delete many documents by primary key in one call tags: - index x-app: index x-public: true /v1/index/indexes/{uid}/documents/{id}: delete: description: 'Deletes one document by its primary key. Removes the document from the caller''s own org and answers the dialect''s EnqueuedTask. Deleting a key that is not there succeeds, so a client reconciling its own corpus can delete without checking first. The 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of later work: the document is already gone when this answers.' operationId: delete_index_indexes_by_uid_documents_by_id parameters: - in: path name: uid required: true schema: type: string - in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/index.indexEnqueued' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes one document by its primary key. tags: - index x-app: index x-public: true x-tool: true get: description: "Reads one document by its primary key.\n\nAnswers the stored document exactly as it was written \u2014\ \ this surface keeps\ndocuments whole rather than projecting them, so what comes back is what went\nin. A primary\ \ key this index does not hold answers 404 carrying the dialect's\n`document_not_found`; an index this org does not\ \ hold answers\n`index_not_found`, and the two are different facts a client acts on\ndifferently." operationId: get_index_indexes_by_uid_documents_by_id parameters: - in: path name: uid required: true schema: type: string - in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads one document by its primary key. tags: - index x-app: index x-public: true x-tool: true /v1/index/indexes/{uid}/search: post: description: "Searches an index, forgiving typos.\n\nRanks the org's documents in one index against `q` and answers\ \ the matching\ndocuments whole, most relevant first. A prefix matches, so a partial word\nfinds the documents containing\ \ it, and `filter` narrows the result to\ndocuments whose filterable attributes match \u2014 which is how a caller\ \ scopes\nresults to one end user within its own org.\n\n`estimatedTotalHits` is the dialect's name for the count;\ \ every hit is\nmaterialised here, so for this page it is exact. An index this org does not\nhold answers 404 carrying\ \ the dialect's `index_not_found`." operationId: post_index_indexes_by_uid_search parameters: - in: path name: uid required: true schema: type: string requestBody: content: application/json: example: filter: user = alice limit: 20 q: roadmap schema: $ref: '#/components/schemas/index.indexQuery' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/index.indexHits' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Searches an index, forgiving typos. tags: - index x-app: index x-public: true x-tool: true /v1/index/indexes/{uid}/settings: get: description: 'Reads an index''s filterable attributes. Answers the settings subset this surface implements: the attributes a search `filter` may constrain. An index this org does not hold answers 404 carrying the dialect''s `index_not_found`.' operationId: get_index_indexes_by_uid_settings parameters: - in: path name: uid required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/index.indexSettings' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads an index's filterable attributes. tags: - index x-app: index x-public: true x-tool: true patch: description: 'Sets which attributes an index can be filtered on. Replaces the whole filterable set. An attribute not listed here cannot be used in a search `filter`, so this is what makes a per-user or per-tag narrowing possible at all. It CREATES the index when it is missing rather than answering 404, because a Meilisearch client configures settings on an index it has just asked for and a refusal there leaves the client with no index at all. The 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of later work: the setting is already applied when this answers.' operationId: patch_index_indexes_by_uid_settings parameters: - in: path name: uid required: true schema: type: string requestBody: content: application/json: example: filterableAttributes: - user - conversationId schema: $ref: '#/components/schemas/index.indexFilter' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/index.indexEnqueued' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Sets which attributes an index can be filtered on. tags: - index x-app: index x-public: true x-tool: true /v1/index/stats: get: description: "Counts the documents in each of your indexes.\n\nReports every index the caller's own org holds with its\ \ document count, plus\nthe org's total. `isIndexing` is always false because writes here are applied\nbefore their\ \ response \u2014 there is never a background pass to wait on.\n\nThe tenant is the org minted from the VALIDATED\ \ bearer's owner claim, never a\nclient-supplied header, so this counts the caller's own documents and no\nother tenant's.\ \ Without a validated principal the answer is 403 carrying the\ndialect's `invalid_api_key` body." operationId: get_index_stats responses: '200': content: application/json: schema: $ref: '#/components/schemas/index.indexStats' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Counts the documents in each of your indexes. tags: - index x-app: index x-public: true x-tool: true /v1/index/tasks/{uid}: get: description: 'Checks a write task, which has already finished. Always reports `succeeded`. Writes here are applied to SQLite before their EnqueuedTask is returned, so a client polling waitForTask resolves on its first call rather than waiting for a queue that was never there. The three timestamps are the same instant for the same reason. It requires a validated principal but reads no tenant data: the task id it echoes was minted by this process and names nothing about any org.' operationId: get_index_tasks_by_uid parameters: - in: path name: uid required: true schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/index.indexTask' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Checks a write task, which has already finished. tags: - index x-app: index x-public: true x-tool: true /v1/index/version: get: description: 'Identifies the search implementation answering. Reports the dialect''s version shape with `commitSha` naming this implementation rather than a Meilisearch build, so a client that logs the version records which server answered instead of implying a release of software this is not. It requires no principal and reads no tenant data.' operationId: get_index_version responses: '200': content: application/json: schema: $ref: '#/components/schemas/index.indexVersion' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Identifies the search implementation answering. tags: - index x-app: index x-public: true x-tool: true /v1/ingress/middlewares: get: description: 'Returns every edge transform the caller''s org has configured, ordered by id. A route names the ones it wants, in order.' operationId: get_ingress_middlewares responses: '200': content: application/json: schema: $ref: '#/components/schemas/ingress.ingressMiddlewares' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns every edge transform the caller's org has configured, ordered by id. tags: - ingress x-app: ingress x-public: true x-tool: true post: description: 'Creates or replaces one edge transform and hot-applies it. POST mints an id when the body omits one; PUT takes the id from the URL, which wins over any id in the body. type must be one of redirectScheme, stripPrefix, addPrefix or headers, and stripPrefix/addPrefix each require their config key.' operationId: post_ingress_middlewares requestBody: content: application/json: example: config: prefixes: /api id: strip-api type: stripPrefix schema: $ref: '#/components/schemas/ingress.Middleware' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/ingress.Middleware' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates or replaces one edge transform and hot-applies it. tags: - ingress x-app: ingress x-public: true x-tool: true /v1/ingress/middlewares/{id}: delete: description: 'Removes one of the caller org''s edge transforms and hot-applies the change. Routes still naming it stop being served (they compile as skipped) until they name a transform that exists. Answers 204; an id this org does not hold is 404.' operationId: delete_ingress_middlewares_by_id parameters: - description: ID is the object to act on, from the path. example: strip-api in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one of the caller org's edge transforms and hot-applies the change. tags: - ingress x-app: ingress x-public: true x-tool: true get: description: Returns one of the caller org's edge transforms by id. operationId: get_ingress_middlewares_by_id parameters: - description: ID is the object to act on, from the path. example: strip-api in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/ingress.Middleware' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one of the caller org's edge transforms by id. tags: - ingress x-app: ingress x-public: true x-tool: true put: description: 'Creates or replaces one edge transform and hot-applies it. POST mints an id when the body omits one; PUT takes the id from the URL, which wins over any id in the body. type must be one of redirectScheme, stripPrefix, addPrefix or headers, and stripPrefix/addPrefix each require their config key.' operationId: put_ingress_middlewares_by_id parameters: - description: 'ID identifies the transform within the org: [A-Za-z0-9-_.], at most 128 chars. A create that omits it gets a generated one. Routes reference it by this id.' example: strip-api in: path name: id required: true schema: type: string requestBody: content: application/json: example: config: prefixes: /api id: strip-api type: stripPrefix schema: $ref: '#/components/schemas/ingress.Middleware' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/ingress.Middleware' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates or replaces one edge transform and hot-applies it. tags: - ingress x-app: ingress x-public: true x-tool: true /v1/ingress/routes: get: description: 'Returns every routing rule the caller''s org has configured, ordered by id. A route maps an exact Host (and optional path prefix) to a service.' operationId: get_ingress_routes responses: '200': content: application/json: schema: $ref: '#/components/schemas/ingress.ingressRoutes' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns every routing rule the caller's org has configured, ordered by id. tags: - ingress x-app: ingress x-public: true x-tool: true post: description: "Creates or replaces one routing rule and hot-applies the new table \u2014\nthere is no config file and\ \ no restart. POST mints an id when the body omits\none; PUT takes the id from the URL, which wins over any id in\ \ the body. A\nroute's host is a GLOBALLY unique DNS claim: a host another org's route already\nholds is refused 409,\ \ so no tenant can hijack another's hostname." operationId: post_ingress_routes requestBody: content: application/json: example: host: app.example.com id: web service: app-pool tls: true schema: $ref: '#/components/schemas/ingress.Route' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/ingress.Route' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Creates or replaces one routing rule and hot-applies the new table \u2014 there is no config file and no restart." tags: - ingress x-app: ingress x-public: true x-tool: true /v1/ingress/routes/{id}: delete: description: 'Removes one of the caller org''s routing rules and hot-applies the shrunken table, freeing its host for another claim. Answers 204; an id this org does not hold is 404.' operationId: delete_ingress_routes_by_id parameters: - description: ID is the object to act on, from the path. example: web in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one of the caller org's routing rules and hot-applies the shrunken table, freeing its host for another claim. tags: - ingress x-app: ingress x-public: true x-tool: true get: description: Returns one of the caller org's routing rules by id. operationId: get_ingress_routes_by_id parameters: - description: ID is the object to act on, from the path. example: a1b2c3d4e5f60718 in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/ingress.Route' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one of the caller org's routing rules by id. tags: - ingress x-app: ingress x-public: true x-tool: true put: description: "Creates or replaces one routing rule and hot-applies the new table \u2014\nthere is no config file and\ \ no restart. POST mints an id when the body omits\none; PUT takes the id from the URL, which wins over any id in\ \ the body. A\nroute's host is a GLOBALLY unique DNS claim: a host another org's route already\nholds is refused 409,\ \ so no tenant can hijack another's hostname." operationId: put_ingress_routes_by_id parameters: - description: 'ID identifies the route within the org: [A-Za-z0-9-_.], at most 128 chars. A create that omits it gets a generated one.' example: web in: path name: id required: true schema: type: string requestBody: content: application/json: example: host: app.example.com id: web service: app-pool tls: true schema: $ref: '#/components/schemas/ingress.Route' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/ingress.Route' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Creates or replaces one routing rule and hot-applies the new table \u2014 there is no config file and no restart." tags: - ingress x-app: ingress x-public: true x-tool: true /v1/ingress/services: get: description: 'Returns every backend pool the caller''s org has configured, ordered by id. A service is the weighted round-robin target a route dispatches to.' operationId: get_ingress_services responses: '200': content: application/json: schema: $ref: '#/components/schemas/ingress.ingressServices' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns every backend pool the caller's org has configured, ordered by id. tags: - ingress x-app: ingress x-public: true x-tool: true post: description: 'Creates or replaces one backend pool and hot-applies it. POST mints an id when the body omits one; PUT takes the id from the URL, which wins over any id in the body. A pool needs at least one backend and every backend URL must be http(s)://host[:port].' operationId: post_ingress_services requestBody: content: application/json: example: backends: - url: http://10.0.0.7:8000 weight: 1 id: app-pool schema: $ref: '#/components/schemas/ingress.Upstream' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/ingress.Upstream' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates or replaces one backend pool and hot-applies it. tags: - ingress x-app: ingress x-public: true x-tool: true /v1/ingress/services/{id}: delete: description: 'Removes one of the caller org''s backend pools and hot-applies the change. Routes still pointing at it stop being served (they compile as skipped) until they name a pool that exists. Answers 204; an id this org does not hold is 404.' operationId: delete_ingress_services_by_id parameters: - description: ID is the object to act on, from the path. example: app-pool in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one of the caller org's backend pools and hot-applies the change. tags: - ingress x-app: ingress x-public: true x-tool: true get: description: Returns one of the caller org's backend pools by id. operationId: get_ingress_services_by_id parameters: - description: ID is the object to act on, from the path. example: app-pool in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/ingress.Upstream' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one of the caller org's backend pools by id. tags: - ingress x-app: ingress x-public: true x-tool: true put: description: 'Creates or replaces one backend pool and hot-applies it. POST mints an id when the body omits one; PUT takes the id from the URL, which wins over any id in the body. A pool needs at least one backend and every backend URL must be http(s)://host[:port].' operationId: put_ingress_services_by_id parameters: - description: 'ID identifies the pool within the org: [A-Za-z0-9-_.], at most 128 chars. A create that omits it gets a generated one. Routes reference it by this id.' example: app-pool in: path name: id required: true schema: type: string requestBody: content: application/json: example: backends: - url: http://10.0.0.7:8000 weight: 1 id: app-pool schema: $ref: '#/components/schemas/ingress.Upstream' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/ingress.Upstream' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates or replaces one backend pool and hot-applies it. tags: - ingress x-app: ingress x-public: true x-tool: true /v1/ingress/status: get: description: 'Reports the ingress edge''s live posture: the role this instance runs in (app or edge), whether its listeners are bound and on which addresses, the ACME posture (staging flag and certificate cache directory), how many hosts the compiled route table currently serves, and how many the ACME HostPolicy will issue a certificate for.' operationId: get_ingress_status responses: '200': content: application/json: schema: $ref: '#/components/schemas/ingress.ingressStatus' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Reports the ingress edge''s live posture: the role this instance runs in (app or edge), whether its listeners are bound and on which addresses, the ACME posture (staging flag and certificate cache directory), how many hosts the compiled route table currently serves, and how many the ACME HostPolicy will issue a certificate for.' tags: - ingress x-app: ingress x-public: true x-tool: true /v1/ingress/tls: get: description: 'Returns the caller org''s ACME intent together with the edge-wide TLS facts it lands in: which role this instance runs in, whether its listeners are bound, every host the ACME HostPolicy will issue a certificate for (the union across ALL orgs of TLS-marked routes and configured extraHosts, because one process holds one certificate cache), and the ACME directory and account email the process was started with.' operationId: get_ingress_tls responses: '200': content: application/json: schema: $ref: '#/components/schemas/ingress.ingressTLS' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the caller org''s ACME intent together with the edge-wide TLS facts it lands in: which role this instance runs in, whether its listeners are bound, every host the ACME HostPolicy will issue a certificate for (the union across ALL orgs of TLS-marked routes and configured extraHosts, because one process holds one certificate cache), and the ACME directory and account email the process was started with.' tags: - ingress x-app: ingress x-public: true x-tool: true put: description: "Replaces the caller org's ACME intent and hot-applies what can be\nhot-applied. extraHosts are normalized\ \ and validated, then feed the ACME\nHostPolicy on the reload this op performs, alongside the per-route tls flags.\n\ acmeEmail and staging bind an ACME account for the lifetime of an edge process,\nso they only take effect when the\ \ edge (re)starts \u2014 the returned note says so." operationId: put_ingress_tls requestBody: content: application/json: example: extraHosts: - www.example.com schema: $ref: '#/components/schemas/ingress.TLSConfig' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/ingress.TLSConfig' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Replaces the caller org's ACME intent and hot-applies what can be hot-applied. tags: - ingress x-app: ingress x-public: true x-tool: true /v1/kms/auth/login: post: description: "Exchanges a machine credential for an IAM bearer token.\n\nTakes a tenant's machine credential \u2014\ \ a client id and client secret \u2014 and\nreturns an owner-scoped IAM access token with its lifetime, which is the\n\ bearer the caller then carries on the org-scoped secret operations.\n\nIt is deliberately public and unauthenticated,\ \ because it IS the credential\nexchange and runs before any principal exists. That makes it the one route in\nthis\ \ subsystem rate-limited PER SOURCE IP, keyed on the real TCP peer rather\nthan on any caller-supplied header, and\ \ body-capped in the same place.\n\nThe submitted secret is never logged and never echoed, and failures collapse\n\ to one clean status with no upstream detail: 401 when the credential does not\nauthenticate, 502 when the identity\ \ provider is unreachable, 503 when no\nissuer is configured. That is on purpose \u2014 a richer error would be a\ \ validity\noracle for guessed credentials." operationId: post_kms_auth_login requestBody: content: application/json: example: clientId: kms-operator clientSecret: "\u2026" schema: $ref: '#/components/schemas/kms.kmsLogin' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/kms.kmsToken' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Exchanges a machine credential for an IAM bearer token. tags: - kms x-app: kms x-public: true x-tool: true /v1/kms/config: get: description: "Returns the runtime configuration for the KMS console.\n\nWhat the console needs before anyone has signed\ \ in: the brand, the OIDC issuer\nit authenticates against, the API base for this subsystem and the path of the\n\ login exchange.\n\nPublic on purpose, and it holds nothing sensitive \u2014 it is deliberately kept\nunder this subsystem's\ \ own namespace rather than under an admin prefix, so a\ngateway that admin-gates the admin routes cannot break the\ \ console's\nlegitimate pre-login fetch." operationId: get_kms_config responses: '200': content: application/json: schema: $ref: '#/components/schemas/kms.kmsConfig' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the runtime configuration for the KMS console. tags: - kms x-app: kms x-public: true x-tool: true /v1/kms/health: get: description: "Reports whether this broker can actually serve secrets.\n\nA real readiness probe, not a liveness stub:\ \ 200 only when the store is open\nAND a master key is configured, with `signing` reporting whether signing keys\n\ are set up too. Anything less answers 503 with `ready:false` and the reason \u2014\nno in-process store, or no master\ \ key \u2014 which are exactly the two states in\nwhich the secret operations refuse.\n\nNot token-gated, because\ \ the platform must be able to probe it without a\ncredential. It reports the broker's configuration state only; no\ \ secret, no\nkey material and no tenant name appears in it." operationId: get_kms_health responses: '200': content: application/json: schema: $ref: '#/components/schemas/kms.kmsHealth' description: ok '503': content: application/json: schema: $ref: '#/components/schemas/kms.kmsHealth' description: service unavailable default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports whether this broker can actually serve secrets. tags: - kms x-app: kms x-public: true x-tool: true /v1/kms/secrets: get: description: "Lists the secrets your org holds, without their values.\n\nReturns the METADATA of the caller's own secrets:\ \ each one's name, path,\nenvironment and sealing scheme. No value and no ciphertext is included \u2014 this\noperation\ \ exists to enumerate what is held, and reading a value is a separate,\nper-secret call.\n\nScoped to the caller's\ \ own org and nothing else, structurally: there is no org\nin the path, the store root is derived from the validated\ \ org claim, and a\ncaller therefore has no way to name another tenant's namespace. `path` narrows\nto a subpath and\ \ `env` selects the environment; both are also accepted under\nthe operator's spellings, `secretPath` and `environment`.\ \ An omitted `env`\nmeans every environment and an omitted `path` means the whole org, because a\ndefault here reported\ \ a populated store as empty.\n\nAdmission is fail-closed and in order: a validated member, an org that is a\nDNS-1123\ \ label, and a store holding a master key \u2014 403, 400 and 503\nrespectively, all decided before any record is\ \ touched." operationId: get_kms_secrets parameters: - description: "Env selects the environment, which is part of a secret's storage key.\nOMITTED means EVERY environment\ \ \u2014 this is the enumeration surface, so it\nmust be able to answer \"what is in here\" without being told where\ \ to look." in: query name: env required: false schema: type: string - description: 'Environment is the KMS operator''s spelling of Env, accepted so one caller need not learn the other''s vocabulary. Env wins when both are sent.' in: query name: environment required: false schema: type: string - description: 'Path narrows the listing to one subtree beneath the caller''s org root, as a `/`-separated path such as `/ci`. OMITTED means the whole org.' in: query name: path required: false schema: type: string - description: 'SecretPath is the KMS operator''s spelling of Path. Path wins when both are sent.' in: query name: secretPath required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/kms.kmsSecrets' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the secrets your org holds, without their values. tags: - kms x-app: kms x-public: true x-tool: true post: description: "Stores or replaces one secret in your org.\n\nUpserts one secret under the caller's own org. The value\ \ is sealed before it\nis written \u2014 a fresh per-secret data key, itself wrapped by the master key \u2014\nso\ \ plaintext never reaches disk. The receipt confirms the name and environment\nthat were written and does not echo\ \ the value.\n\n`env` is REQUIRED on a write and has no default, which is the rule most easily\ngot wrong here: reads\ \ and deletes still fall back to the default environment\nfor older callers, but a write must not, because the environment\ \ is part of\nthe storage key. A silently defaulted write lands in a bucket the readers that\nresolve project, environment\ \ and path never look in, and the stale value keeps\nbeing served \u2014 so the write fails loudly instead.\n\n`name`\ \ is required, `path` is an optional subpath beneath the org root, and\nthe org is taken from the validated claim\ \ rather than the body.\n\nOpens only for SuperAdmin holding an access token, audited, or for a holder\na declared\ \ WRITE grant covers at this exact path, key and environment. Org\nadmin confers nothing here. Fail-closed admission,\ \ in order: a validated\nmember, well-formed org, master key present, well-formed input, then the\ngrant \u2014 401/403,\ \ 400, 503, 400 and 403, all decided before any record is\ntouched.\n\nA value this deployment seals on the org's\ \ behalf \u2014 an MCP server's\ncredential, a wallet's signing key, a delivery provider's token \u2014 is written\n\ by the app that holds it, through that app's own operation, and is never\nreplaced here for membership or org admin,\ \ whatever the grant declaration\nsays about enforcement." operationId: post_kms_secrets requestBody: content: application/json: example: env: prod name: deploy-token path: /ci value: s3cr3t schema: $ref: '#/components/schemas/kms.kmsPut' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/kms.kmsStored' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Stores or replaces one secret in your org. tags: - kms x-app: kms x-public: true x-tool: true /v1/knowledge/connectors: get: description: 'Returns every supported knowledge connector with THIS org''s connection state and the REAL number of documents each has ingested into the org''s store. A provider that is configured for the deployment but not yet connected appears as disconnected, so the console can offer a Connect button. No secret is ever returned.' operationId: get_knowledge_connectors responses: '200': content: application/json: schema: $ref: '#/components/schemas/knowledge.kbConnectorsOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns every supported knowledge connector with THIS org's connection state and the REAL number of documents each has ingested into the org's store. tags: - knowledge x-app: knowledge x-public: true x-tool: true /v1/knowledge/connectors/catalog: get: description: "Returns the ONE catalog of everything a caller can\nconnect: every first-party connector and every long-tail\ \ one, in a single list\nsorted by provider. `configured` reports whether this deployment holds OAuth\ncredentials\ \ for a source, so the console can show Connect rather than a dead\nbutton, and `kind` is a badge only \u2014 the\ \ connect and sync lifecycle is\nidentical for both. The catalog itself is org-independent; a validated\nprincipal\ \ is still required. It is metadata only: no secret is ever returned." operationId: get_knowledge_connectors_catalog responses: '200': content: application/json: example: connectors: - configured: true description: Repositories, READMEs, and issues. displayName: GitHub kind: native provider: github schema: $ref: '#/components/schemas/knowledge.catalogOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the ONE catalog of everything a caller can connect: every first-party connector and every long-tail one, in a single list sorted by provider.' tags: - knowledge x-app: knowledge x-public: true x-tool: true /v1/knowledge/connectors/{provider}: delete: description: "Revokes a connection: it tombstones the stored credential\nso a later sync cannot reuse it, removes this\ \ provider's passages from the\norg's store, and marks the connector disconnected. The documents already\ningested\ \ stay in the org's store \u2014 they are the org's own data \u2014 but stop being\nretrievable by search; a caller\ \ deletes them through the document surface." operationId: delete_knowledge_connectors_by_provider parameters: - description: 'Provider is the connector to act on: github, slack, google or notion.' in: path name: provider required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/knowledge.connectionOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Revokes a connection: it tombstones the stored credential so a later sync cannot reuse it, removes this provider''s passages from the org''s store, and marks the connector disconnected.' tags: - knowledge x-app: knowledge x-public: true x-tool: true /v1/knowledge/connectors/{provider}/callback: get: description: "CompleteConnectorOAuth finishes an OAuth connection: it exchanges the\nprovider's code for a token, seals\ \ that token in KMS, and records the\nconnection. THE ORG COMES FROM THE SIGNED STATE, not from a header and not from\n\ the provider, so an attacker cannot bind their own account to someone else's\norg \u2014 a tampered, expired or foreign-provider\ \ state is refused outright. The\ntoken itself is never returned, never written into the document, and never\nlogged;\ \ the document holds only its KMS path." operationId: get_knowledge_connectors_by_provider_callback parameters: - description: Provider is the connector completing its flow, from the path. in: path name: provider required: true schema: type: string - description: Code is the provider's authorization code, exchanged for a token. in: query name: code required: false schema: type: string - description: State is the org-bound value this server signed at connect time. in: query name: state required: false schema: type: string - description: Error is the provider's denial reason when the user refused consent. in: query name: error required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/knowledge.connectionOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'CompleteConnectorOAuth finishes an OAuth connection: it exchanges the provider''s code for a token, seals that token in KMS, and records the connection.' tags: - knowledge x-app: knowledge x-public: true x-tool: true /v1/knowledge/connectors/{provider}/connect: get: description: "StartConnectorOAuth returns the provider authorize URL the console opens to\nconnect this org's account.\ \ There is no server-side redirect \u2014 the console\nstays in control of the navigation. The URL carries a state\ \ this server SIGNED\nover the caller's validated org, so the connection the callback completes can\nonly ever land\ \ in that org." operationId: get_knowledge_connectors_by_provider_connect parameters: - description: 'Provider is the connector to act on: github, slack, google or notion.' in: path name: provider required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/knowledge.kbAuthorizeOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: StartConnectorOAuth returns the provider authorize URL the console opens to connect this org's account. tags: - knowledge x-app: knowledge x-public: true x-tool: true /v1/knowledge/connectors/{provider}/sync: post: description: "Pulls the provider's documents for the caller's org and files\nthem as knowledge sources, which the store's\ \ own hook then indexes \u2014 so a\nsynced document is retrievable exactly like a hand-written page. The org is the\n\ validated tenant and the credential is read from KMS, so an org can only ever\nsync its own connection. A provider\ \ failure is reported honestly (502) and\nrecorded on the connector rather than silently swallowed." operationId: post_knowledge_connectors_by_provider_sync parameters: - description: 'Provider is the connector to act on: github, slack, google or notion.' in: path name: provider required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/knowledge.kbSyncOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Pulls the provider's documents for the caller's org and files them as knowledge sources, which the store's\ \ own hook then indexes \u2014 so a synced document is retrievable exactly like a hand-written page." tags: - knowledge x-app: knowledge x-public: true x-tool: true /v1/knowledge/graph: get: description: 'Returns the caller org''s knowledge as a node/edge graph shaped for a force-directed renderer: pages, memories and synced sources as nodes; the page parent tree, the wikilinks between pages, and each source''s connector provenance as edges. Wikilink targets are resolved HERE by title or slug, so a rename never needs an edge rewrite and a link that matches no page renders as its own "unresolved" node instead of vanishing. ?project= narrows it. A store outage degrades to an honest empty graph, never a 5xx.' operationId: get_knowledge_graph parameters: - description: Project narrows the graph to one project scope. Empty reads the whole org. in: query name: project required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/knowledge.graphOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the caller org''s knowledge as a node/edge graph shaped for a force-directed renderer: pages, memories and synced sources as nodes; the page parent tree, the wikilinks between pages, and each source''s connector provenance as edges.' tags: - knowledge x-app: knowledge x-public: true x-tool: true /v1/knowledge/import: post: description: "Ingests an uploaded export as a tree of kb.page documents with its link structure intact. `?format=` picks\ \ the normalizer \u2014 obsidian, notion, roam or evernote \u2014 and the export arrives as a multipart `file` part,\ \ or as the raw request body when there is no multipart part: an Obsidian or Notion vault zip, a Roam JSON (raw or\ \ inside the zip Roam downloads), or an Evernote .enex.\n\nThe pages are filed through the SAME ingest path a connector\ \ sync uses, so the kb.page hook indexes each one for retrieval AND extracts its `[[wikilinks]]` into kb.link edges\ \ \u2014 the imported vault is searchable and its graph is navigable without a second pass. Parents are filed before\ \ their children, and each page takes a slug unique within the org (suffixed -2, -3, \u2026 on collision), so a re-import\ \ adds pages rather than overwriting the ones already there.\n\nScoped to the caller's validated org; `?project=`\ \ narrows every imported page to one project. No validated principal is 403, and an org that has not installed the\ \ kb module is refused with the install call to make first. The bounds are 64 MB per upload, 5000 pages and 8 MB per\ \ archive entry: pages past the five-thousandth are dropped and a larger entry is truncated at its bound, and a page\ \ the store rejects is skipped \u2014 so the answer's `imported` count is what was actually filed, not what was sent." operationId: post_knowledge_import summary: Import an Obsidian, Notion, Roam or Evernote export into the org's knowledge base tags: - knowledge x-app: knowledge x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/knowledge/reindex: post: description: "Rebuilds the caller org's retrieval from its documents: every passage\nis removed, and every page, memory\ \ and source is cut into passages and\nembedded again with the configured model; the lexical index is reconciled to\n\ the same documents. It is what an operator runs after the embedding model\nchanges \u2014 passages of another model\ \ are never compared with the query, so\nuntil then they are unread \u2014 and what puts an org's retrieval right\ \ after an\noutage of the ai plane. It requires ORG ADMIN and runs inline: an org's\nknowledge is a few thousand documents,\ \ and the answer is the count.\n\nThe request has no body. Response: {\"vectors\": 412, \"lexical\": 412, \"removed\"\ : 3, \"failed\": 0}" operationId: post_knowledge_reindex responses: '200': content: application/json: schema: $ref: '#/components/schemas/knowledge.reindexOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Rebuilds the caller org''s retrieval from its documents: every passage is removed, and every page, memory and source is cut into passages and embedded again with the configured model; the lexical index is reconciled to the same documents.' tags: - knowledge x-app: knowledge x-public: true x-tool: true /v1/knowledge/search: post: description: "Runs a hybrid search over the caller org's own knowledge \u2014\nits wiki pages, its agent memories and\ \ everything its connectors have synced.\nThis is the RAG entry point: an agent asks \"what does this org know about\ \ X\"\nand the org's OWN knowledge answers. Two legs run and are fused by reciprocal\nrank: a semantic leg that embeds\ \ the query and compares it with every passage\nof every document in reach (a document is cut into passages of about\ \ 2000\nbytes, so a fact deep in a long page is found), and a keyword leg over the\norg's knowledge index. The org\ \ comes from the validated principal and its\npassages live in its own database, so cross-tenant retrieval is impossible.\ \ A\nfailed leg answers 200 with what the other leg found and degraded=true, never\na 5xx." operationId: post_knowledge_search requestBody: content: application/json: example: limit: 5 query: how do we rotate the signing key schema: $ref: '#/components/schemas/knowledge.searchIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/knowledge.searchOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Runs a hybrid search over the caller org's own knowledge \u2014 its wiki pages, its agent memories and everything\ \ its connectors have synced." tags: - knowledge x-app: knowledge x-public: true x-tool: true /v1/kv/{bucket}: delete: description: "Removes one bucket of the caller's org \u2014 every key and every\nrevision with it \u2014 and answers\ \ 204 with no body. 404 when the org has no\nbucket of that name." operationId: delete_kv_by_bucket parameters: - description: Bucket is the bucket's name, from the path. in: path name: bucket required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Removes one bucket of the caller's org \u2014 every key and every revision with it \u2014 and answers 204\ \ with no body." tags: - kv x-app: kv x-public: true x-tool: true post: description: 'Creates a KV bucket and returns it. A bucket is keyed state on the same durable plane as the streams: each key holds up to History revisions, entries can expire by TTL, and watchers on the NATS port see every write. 409 when the org already has a bucket of that name.' operationId: post_kv_by_bucket parameters: - description: "Bucket is the bucket's name within the org, from the path: 1\u201364 of\n[A-Za-z0-9_], no dash." in: path name: bucket required: true schema: type: string requestBody: content: application/json: example: history: 5 ttl: 3600 schema: $ref: '#/components/schemas/kv.bucketWrite' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/kv.bucketRecord' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a KV bucket and returns it. tags: - kv x-app: kv x-public: true x-tool: true /v1/kv/{bucket}/{key}: delete: description: "Delete removes one key \u2014 a delete marker in the key's history, so watchers\nsee it and Get answers\ \ 404 \u2014 and answers 204 with no body. 404 when the\nbucket does not exist." operationId: delete_kv_by_bucket_by_key parameters: - description: Bucket is the bucket, from the path. in: path name: bucket required: true schema: type: string - description: Key is the key, from the path. in: path name: key required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Delete removes one key \u2014 a delete marker in the key's history, so watchers see it and Get answers 404\ \ \u2014 and answers 204 with no body." tags: - kv x-app: kv x-public: true x-tool: true get: description: 'Returns one key''s current value and revision. 404 when the bucket does not exist, the key was never written, or its latest revision is a delete.' operationId: get_kv_by_bucket_by_key parameters: - description: Bucket is the bucket, from the path. in: path name: bucket required: true schema: type: string - description: Key is the key, from the path. in: path name: key required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/kv.kvEntry' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one key's current value and revision. tags: - kv x-app: kv x-public: true x-tool: true put: description: 'Sets one key to one value and returns the revision the write created. Writes are versioned: each put is a new revision and the bucket retains up to its History of them per key.' operationId: put_kv_by_bucket_by_key parameters: - description: Bucket is the bucket, from the path. in: path name: bucket required: true schema: type: string - description: Key is the key, from the path. in: path name: key required: true schema: type: string requestBody: content: application/json: example: value: '{"theme":"dark"}' schema: $ref: '#/components/schemas/kv.kvWrite' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/kv.kvAck' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Sets one key to one value and returns the revision the write created. tags: - kv x-app: kv x-public: true x-tool: true /v1/kv/{bucket}/{key}/history: get: description: "Returns one key's retained revisions, oldest first \u2014 every put and\nevery delete marker up to the\ \ bucket's History depth. 404 when the bucket\ndoes not exist or the key was never written." operationId: get_kv_by_bucket_by_key_history parameters: - description: Bucket is the bucket, from the path. in: path name: bucket required: true schema: type: string - description: Key is the key, from the path. in: path name: key required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/kv.kvPage' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one key's retained revisions, oldest first \u2014 every put and every delete marker up to the bucket's\ \ History depth." tags: - kv x-app: kv x-public: true x-tool: true /v1/label: get: description: "Reads the assertions this tenant has recorded, newest event first.\n\nIt reads the RECORD \u2014 the tenant's\ \ own store \u2014 and not the columnar copy, so\nwhat it returns is what would be produced in an audit. Narrow it\ \ by entity, by\nasserter, or by event window." operationId: riskLabels parameters: - description: Kind and Subject narrow to one entity. in: query name: kind required: false schema: type: string - in: query name: subject required: false schema: type: string - description: "Source narrows to one asserter \u2014 the read that answers \"what has commerce\ntold us\", separately\ \ from \"what has an analyst told us\"." in: query name: source required: false schema: type: string - description: From and To bound the EVENT time, half-open, RFC 3339. in: query name: from required: false schema: type: string - in: query name: to required: false schema: type: string - description: Limit caps the page. Out of range takes the plane's own bound. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/label.riskLabelsOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Read the assertions this tenant has recorded tags: - label x-app: label x-public: true x-tool: true post: description: "Records a batch of ground truth against the entities it judges.\n\nEach assertion carries TWO times \u2014\ \ when the judged event happened, and when\nthe assertion became knowable \u2014 and both are required. The second\ \ is what\nkeeps a chargeback that landed in June out of a model that had to decide in\nFebruary.\n\nIt is idempotent\ \ on the CONTENT of an assertion, so a webhook that redelivers\nis safe. It never overwrites: a source that corrects\ \ itself later files a NEW\nassertion, which wins from the moment it became knowable and leaves every\nearlier observation\ \ instant seeing exactly what it saw.\n\nThe asserter is stamped from the validated credential and is not a body field." operationId: riskLabel requestBody: content: application/json: schema: $ref: '#/components/schemas/label.riskLabelIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/label.riskLabelOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Assert ground truth about events tags: - label x-app: label x-public: true x-tool: true /v1/label/coverage: get: description: 'Reports how much of a window has matured and how much of that is judged, per source. It is the gate on training. A supervised fit over a window whose judged count is near zero produces a number, and the number is meaningless; this op is what lets that be stated before the fit rather than discovered after it. It reads the RECORD plane and folds every assertion at that event''s OWN as-of, so the counts obey exactly the leakage rule a materialisation would. It counts only what was ASSERTED: what share of the whole event STREAM carries a label is a question about the feature plane''s denominator and is not answerable here.' operationId: riskLabelCoverage parameters: - description: "From and To bound the EVENT window, half-open, RFC 3339.\n\nUnstated, the window is the 90 days ENDING\ \ where maturity begins \u2014 `to` is\nthe horizon ago, not now. A default window running to now under a default\n\ horizon could not contain one matured event, so every count below it would\nbe zero however much ground truth the\ \ tenant held." in: query name: from required: false schema: type: string - in: query name: to required: false schema: type: string - description: 'Horizon is the maturity horizon in days the coverage is measured under. Unstated takes 120. It also moves the default window, which ends where maturity begins.' in: query name: horizon required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/label.riskLabelCoverage' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: How much of the window has matured, and how much of that is judged tags: - label x-app: label x-public: true x-tool: true /v1/label/dispose: post: description: 'Applies this tenant''s retention, and only this tenant''s. It is bounded three ways, each a compliance property rather than a convenience. It refuses a boundary younger than the platform floor, because a label can be the input to an adverse action and five years is what the retention ledger holds such a record for. It never touches a record under litigation hold. And it disposes of whole records rather than redacting fields. It removes the derived columnar copy BEFORE the record, and refuses the whole disposal if the warehouse cannot be reached. The other order would leave rows in the warehouse that nothing can identify any more, which is a disposal that did not happen and says it did.' operationId: riskDisposeLabels requestBody: content: application/json: schema: $ref: '#/components/schemas/label.riskDisposeIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/label.riskDisposeOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Dispose of this tenant's expired assertions, whole records only tags: - label x-app: label x-public: true x-tool: true /v1/label/hold: post: description: "Places or releases a litigation hold on named records.\n\nA hold is a fact about the RECORD, not about\ \ the world: it says retention may\nnot dispose of this row, and it asserts nothing about what happened. So it is\n\ not a field on an assertion and it is not folded into the content digest \u2014\ncarried there it was silently a no-op\ \ on any record that already existed, since\nre-filing the same assertion with a hold flag produced the same digest,\ \ the\ninsert was ignored, and the caller was answered `duplicate` while the hold it\nasked for was never placed.\ \ This op is the one way a hold moves, in either\ndirection, and the move is written to the audit log.\n\nEvery named\ \ id is this tenant's or is nothing. The statement runs against the\ntenant's own file, which holds no other tenant's\ \ rows and has no column that\ncould name one." operationId: riskHoldLabels requestBody: content: application/json: schema: $ref: '#/components/schemas/label.riskHoldIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/label.riskHoldOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Place or release a litigation hold on named records tags: - label x-app: label x-public: true x-tool: true /v1/label/resolve: post: description: "Answers, for each named event, which assertion was in force AS OF that\nevent's own horizon \u2014 and\ \ what disagreed with it.\n\nThis is the join surface: the dataset materialiser calls it to attach ground\ntruth to\ \ training rows, and the evaluator calls it to score a past decision\nagainst what was knowable when the decision\ \ had to be made. One mechanism for\nboth, so a model can never be trained under one leakage rule and scored under\n\ another.\n\nThree answers are distinct and all three are honest: a resolved label, an\nevent that has not matured,\ \ and a matured event nobody has judged. The last is\nnever reported as unproductive." operationId: riskResolveLabels requestBody: content: application/json: schema: $ref: '#/components/schemas/label.riskResolveIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/label.riskResolveOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Resolve the label in force for named events, as of each event's own horizon tags: - label x-app: label x-public: true x-tool: true /v1/label/vocabulary: get: description: "Publishes the closed vocabularies and the precedence rule that\nresolves a conflict between two sources.\n\ \nA precedence rule nobody can read is a rule nobody can audit or dispute, and\nthe whole defensibility of a contested\ \ label rests on being able to say why\none assertion beat another. The order returned here is derived from the same\n\ declaration the resolver reads \u2014 it is not a description of it." operationId: riskLabelVocabulary responses: '200': content: application/json: schema: $ref: '#/components/schemas/label.riskLabelVocabulary' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: The closed vocabularies and the precedence rule that resolves a conflict tags: - label x-app: label x-public: true x-tool: true /v1/leaderboard: get: description: 'Ranks AI usage over a window, either the users of the caller''s own org or organizations against each other, and always reports the caller''s own standing even when it falls outside the returned page. Identities are private by default: a caller sees themselves, plus the peers or orgs that opted into public listing, and only an admin sees their own org''s members named. Cross-org spend is restricted to platform admins. When the warehouse is not connected the board answers empty with available=false rather than a fabricated rank.' operationId: get_leaderboard parameters: - description: 'Scope picks the board: "personal" (default) ranks the caller among their own org''s users, "org" is that same org board named for an admin, "global" ranks organizations against each other.' example: personal in: query name: scope required: false schema: type: string - description: 'Metric is the value ranked: tokens (default), requests, or cost.' example: tokens in: query name: metric required: false schema: type: string - description: 'Period is the window ranked: day, week, month (default) or all.' example: week in: query name: period required: false schema: type: string - description: 'Limit caps the rows returned, clamped to 100. Defaults to 10, which is also what a non-positive or unparseable value takes.' example: 10 in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/leaderboard.LeaderboardView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Ranks AI usage over a window, either the users of the caller's own org or organizations against each other, and always reports the caller's own standing even when it falls outside the returned page. tags: - leaderboard x-app: leaderboard x-public: true x-tool: true /v1/leaderboard/activity: get: description: "Returns the per-day usage series for ONE authorized subject \u2014 the points a\ncontribution heatmap\ \ and a timeline are drawn from, gap-filled so every day in the\nrange is present. Authorization is resolved server-side\ \ from the validated\nprincipal, so a caller can never widen the subject past what they are entitled to:\na non-admin\ \ reads only themselves and their own org. subject=project answers empty\nwith a note, because the usage ledger records\ \ no project column yet. When the\nwarehouse is not connected the series answers empty with available=false rather\n\ than fabricated days." operationId: get_leaderboard_activity parameters: - description: 'Subject is what the series is about: "user" (default), "org" or "project".' example: user in: query name: subject required: false schema: type: string - description: 'ID names the subject within what the caller is entitled to see. Omitted (or "me") it is the caller themselves, or their own org. Another user requires org admin and must belong to the caller''s org; another org requires a SuperAdmin.' in: query name: id required: false schema: type: string - description: From is the first day of the range, "2006-01-02". Defaults to 90 days back. example: '2026-01-01' in: query name: from required: false schema: type: string - description: 'To is the last day of the range, "2006-01-02". Defaults to today; the span is clamped to 366 days.' example: '2026-03-31' in: query name: to required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/leaderboard.ActivityView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the per-day usage series for ONE authorized subject \u2014 the points a contribution heatmap and a\ \ timeline are drawn from, gap-filled so every day in the range is present." tags: - leaderboard x-app: leaderboard x-public: true x-tool: true /v1/leaderboard/optin: get: description: 'Returns the caller''s own public-listing preference and their org''s, each with whether the caller may change it. Public listing is opt-in and private by default, so a fresh caller reads listed=false for both.' operationId: get_leaderboard_optin responses: '200': content: application/json: schema: $ref: '#/components/schemas/leaderboard.optinView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the caller's own public-listing preference and their org's, each with whether the caller may change it. tags: - leaderboard x-app: leaderboard x-public: true x-tool: true put: description: 'Sets the CALLER''s own public-listing preference on the leaderboard. Self only: the row written is keyed by the caller''s validated ledger identity, so this can never edit another member''s visibility whatever the request says. A caller opting in with no handle is given their username, so a listed row never renders as "Anonymous" to its own owner.' operationId: put_leaderboard_optin requestBody: content: application/json: example: handle: ada listed: true schema: $ref: '#/components/schemas/leaderboard.userOptinReq' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/leaderboard.userOptinView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Sets the CALLER's own public-listing preference on the leaderboard. tags: - leaderboard x-app: leaderboard x-public: true x-tool: true /v1/leaderboard/optin/org: put: description: "Sets the ORG's listing on the cross-org global board. Only an admin of\nthe caller's own org \u2014 an\ \ org admin or a platform SuperAdmin \u2014 may change it, and\nthe org written is the caller's validated tenant,\ \ never a value from the request.\nListing consents to publishing the org's usage VOLUME; cross-org spend stays\n\ restricted to platform admins regardless." operationId: put_leaderboard_optin_org requestBody: content: application/json: example: display: Acme listed: true schema: $ref: '#/components/schemas/leaderboard.orgOptinReq' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/leaderboard.orgOptinView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Sets the ORG's listing on the cross-org global board. tags: - leaderboard x-app: leaderboard x-public: true x-tool: true /v1/legal/documents: get: description: "Returns the org's generated documents, newest first, WITHOUT\ntheir rendered content \u2014 fetch one\ \ document to read its body.\n\nThe response is marked no-store: these records name the counterparties an org is\n\ contracting with, and must not sit in a shared cache." operationId: get_legal_documents parameters: - description: Limit bounds the page. Absent or unparseable means the store's own default. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/legal.documentPage' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the org's generated documents, newest first, WITHOUT their rendered content \u2014 fetch one document\ \ to read its body." tags: - legal x-app: legal x-public: true x-tool: true post: description: "Renders a document from a template and the caller's own\nmerge data, seals it in the org's store, and\ \ returns it with its rendered body.\n\nThe render is PURE and deterministic \u2014 no clock, no I/O \u2014 so the\ \ same template\nversion and the same data always produce identical bytes, which is what makes a\ngenerated contract\ \ reproducible. It fails CLOSED on a missing merge field: there\nis no blank-filled contract, only a 400 naming the\ \ fields that were absent. When\nthe template is counsel-review the rendered body opens with the counsel notice,\n\ which no caller can suppress.\n\nThe document is a DRAFT. Hanzo Legal manages documents; it does not give legal\n\ advice and does not determine that a document is valid or sufficient." operationId: post_legal_documents requestBody: content: application/json: example: data: counterparty: AgentCo, LLC date: '2026-07-30' templateId: nda schema: $ref: '#/components/schemas/legal.generateRequest' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/legal.documentReply' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Renders a document from a template and the caller's own merge data, seals it in the org's store, and returns it with its rendered body. tags: - legal x-app: legal x-public: true x-tool: true /v1/legal/documents/{id}: get: description: "Returns one of the org's documents WITH its rendered body. 404\nwhen the org has no document with that\ \ id \u2014 a document is never readable across\norgs.\n\nThe response is marked no-store: the body is contract text,\ \ sealed at rest and\nreturned only to the owning org, and must not sit in a shared cache." operationId: get_legal_documents_by_id parameters: - description: ID is the document's server-minted handle, "doc_"-prefixed. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/legal.documentReply' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one of the org's documents WITH its rendered body. tags: - legal x-app: legal x-public: true x-tool: true /v1/legal/documents/{id}/sign: post: description: "Opens an e-signature request over one document and moves it\nto out_for_signature, returning the provider's\ \ reference for the request.\n\nThe provider is whatever this deployment has wired. The honest default is\n\"manual\"\ : the request is recorded and the org fulfils it out of band \u2014 nothing\nhere fabricates a signature, and the\ \ stub never reports itself complete." operationId: post_legal_documents_by_id_sign parameters: - description: ID is the document to send for signature, from the path. example: "doc_1f\u2026" in: path name: id required: true schema: type: string requestBody: content: application/json: example: id: "doc_1f\u2026" signers: - email: ada@acme.com name: Ada schema: $ref: '#/components/schemas/legal.signRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/legal.signReply' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Opens an e-signature request over one document and moves it to out_for_signature, returning the provider's reference for the request. tags: - legal x-app: legal x-public: true x-tool: true /v1/legal/documents/{id}/sign/complete: post: description: 'Records completion of the signature request opened over a generated document and answers the document with a `signed` flag. The e-sign provider''s own status is consulted FIRST and is the default answer; an explicit `signed` field in the body overrides it. That override is the whole point: the default `manual` provider never self-completes, so a reviewer (or a real provider''s webhook) is what moves the document. A completion flips the document to `signed`, stamps `signedAt`, and writes a `legal.document.signed` audit event; a provider still reporting incomplete answers 200 with the document unchanged, so the call is safe to repeat and never fabricates a signature. Org-scoped and fails closed: a validated principal is required (401 without one), the document is read under the caller''s OWN org so another tenant''s id is a 404, a document with no open signature request is a 400, and a provider whose status call errors is a 502.' operationId: post_legal_documents_by_id_sign_complete parameters: - in: path name: id required: true schema: type: string summary: Record that a generated document's signature request completed tags: - legal x-app: legal x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/legal/filings: get: description: "Returns the org's filing records, newest first \u2014 which documents\nwere filed where, through which\ \ provider, and what the filing's honest status is." operationId: get_legal_filings parameters: - description: Limit bounds the page. Absent or unparseable means the store's own default. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/legal.filingPage' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the org's filing records, newest first \u2014 which documents were filed where, through which provider,\ \ and what the filing's honest status is." tags: - legal x-app: legal x-public: true x-tool: true post: description: 'Records a filing of one or more of the org''s documents with a state or agency, and returns the tracking record. It is a TRACKING record, not an autonomous filing. With no filing partner wired the honest status is "manual" and the note says so: the documents were generated for signature, and the org files them through its registered agent. Nothing here invents a filing id it does not have. Every document id must belong to the caller''s org; one that does not is a 404 naming it, so a filing can never reach across tenants.' operationId: post_legal_filings requestBody: content: application/json: example: documentIds: - "doc_1f\u2026" jurisdiction: DE schema: $ref: '#/components/schemas/legal.filingRequest' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/legal.filingReply' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records a filing of one or more of the org's documents with a state or agency, and returns the tracking record. tags: - legal x-app: legal x-public: true x-tool: true /v1/legal/health: get: description: 'Reports that the legal subsystem is serving and how many built-in templates its catalog carries. It reads no tenant, so a liveness prober that sends no principal is answered rather than refused.' operationId: get_legal_health responses: '200': content: application/json: schema: $ref: '#/components/schemas/legal.legalHealth' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports that the legal subsystem is serving and how many built-in templates its catalog carries. tags: - legal x-app: legal x-public: true x-tool: true /v1/legal/templates: get: description: "Returns the org's effective template catalog: every built-in\ntemplate, with any the org has overridden\ \ replaced by its own latest version.\n\nThe listing carries each template's metadata and its declared MERGE FIELDS\ \ \u2014 the\nkeys a document generation must supply \u2014 but never the template bodies; fetch one\ntemplate to\ \ get its body. Templates in the formation and equity categories are\nmarked counselReview: every document rendered\ \ from them carries a counsel notice,\nand that posture cannot be dropped by an override." operationId: get_legal_templates responses: '200': content: application/json: schema: $ref: '#/components/schemas/legal.templateCatalog' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the org''s effective template catalog: every built-in template, with any the org has overridden replaced by its own latest version.' tags: - legal x-app: legal x-public: true x-tool: true /v1/legal/templates/{id}: get: description: "Returns one template resolved for the caller's org \u2014 the org's\nown override if it has saved one,\ \ else the built-in \u2014 with its full text/template\nbody and its declared merge fields. 404 when neither exists." operationId: get_legal_templates_by_id parameters: - description: ID is the template's stable id, e.g. "nda" or "safe". in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/legal.templateReply' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one template resolved for the caller's org \u2014 the org's own override if it has saved one, else\ \ the built-in \u2014 with its full text/template body and its declared merge fields." tags: - legal x-app: legal x-public: true x-tool: true put: description: "Saves the org's own version of a template \u2014 a custom\nNDA, a house MSA \u2014 and returns it with\ \ its new version number. It takes effect\nfor that org only; other orgs keep the built-in.\n\nTwo boundaries cannot\ \ be crossed here. Overriding a built-in INHERITS its\ncategory and its counsel-review posture, which can be raised\ \ but never dropped;\nand a formation or equity template is counsel-review whatever the caller sends,\nso no org can\ \ generate a securities-class document without the notice.\n\nThe body is validated on save, not at generation: a\ \ template that references an\nUNDECLARED merge field is refused with 400 rather than stored and rendered blank\n\ into a contract months later." operationId: put_legal_templates_by_id parameters: - description: 'ID is the template to override, from the path. Overriding a built-in id inherits that built-in''s category, title and counsel-review posture.' example: nda in: path name: id required: true schema: type: string requestBody: content: application/json: example: body: "\u2026{{.counterparty}}\u2026" fields: - key: counterparty label: Counterparty id: nda title: AgentCo Mutual NDA schema: $ref: '#/components/schemas/legal.templateOverride' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/legal.templateReply' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Saves the org's own version of a template \u2014 a custom NDA, a house MSA \u2014 and returns it with its\ \ new version number." tags: - legal x-app: legal x-public: true x-tool: true /v1/licensing/download/{release}: get: description: "Download resolves a release to its artifact, gated on a valid license.\n\nThe gate is the LICENSE token,\ \ not the IAM bearer: being signed in is not\npermission to download a paid binary \u2014 holding a good license for\ \ it is. The\ntoken must verify against this deployment's public key, be unrevoked, be\nscoped to the release's app,\ \ and carry every feature the release requires.\nPresent it as the `X-License-Token` header (preferred, since a header\ \ does not\nland in proxy logs) or as `?token=`.\n\nThe response pairs the artifact URL with its cosign signature\ \ so the client\nverifies the binary BEFORE trusting it: a signed URL alone proves where the\nbytes came from, not\ \ what they are. A yanked release is 410 Gone." operationId: get_licensing_download_by_release parameters: - in: path name: release required: true schema: type: string - in: header name: X-License-Token required: false schema: type: string - in: query name: token required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/licensing.ReleaseAsset' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Download resolves a release to its artifact, gated on a valid license. tags: - licensing x-app: licensing x-public: true x-tool: true /v1/licensing/fingerprint: post: description: "Fingerprint turns raw device signals into the opaque value that binds a license\nto one machine.\n\nThis\ \ is the anti-copy step: the value returned here is folded into the signed\ntoken, so a token minted with it runs\ \ only on the device it was bound to. The\nderivation is one-way and salted \u2014 the signals are never stored and\ \ never\nechoed back \u2014 so the response is safe to persist client-side and pass to\nissue. Signals too weak to\ \ identify a machine (a hostname alone) are refused\nrather than turned into a binding that would collide with other\ \ machines." operationId: post_licensing_fingerprint requestBody: content: application/json: example: signals: arch: arm64 cpuid: "\u2026" machine_id: "\u2026" os: linux schema: $ref: '#/components/schemas/licensing.FingerprintRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/licensing.FingerprintResponse' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Fingerprint turns raw device signals into the opaque value that binds a license to one machine. tags: - licensing x-app: licensing x-public: true x-tool: true /v1/licensing/healthz: get: description: "Health reports which signer this deployment mints with, and in which env.\n\nIt answers 200 whenever the\ \ process is up: there is nothing downstream to\nprobe, since the KMS is reached only when a token is actually minted.\ \ Its\nvalue is the `signer` field \u2014 `\"signer\":\"local\"` on a production host says\nthat deployment is signing\ \ licenses with a development key, which is a\nmisconfiguration worth paging on rather than a healthy 200." operationId: get_licensing_healthz responses: '200': content: application/json: schema: $ref: '#/components/schemas/licensing.HealthView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Health reports which signer this deployment mints with, and in which env. tags: - licensing x-app: licensing x-public: true x-tool: true /v1/licensing/issue: post: description: "Issue mints a signed license token for a product the caller's org already pays\nfor.\n\nThe order is the\ \ whole security argument: the caller is an IAM-validated\nprincipal, commerce is then asked whether that principal's\ \ ORG holds an ACTIVE\nentitlement for the product, and only then is a token signed \u2014 by the KMS,\nnever by key\ \ material in this process. A product the org does not own answers\n403 and no token. The signed features are the\ \ plan's features verbatim, so the\nengine enforces exactly what was bought, and the expiry is clamped to the\nentitlement's\ \ so a token cannot outlive the subscription that paid for it.\n\nThe token is the credential the engine runs on.\ \ Treat it as a secret." operationId: post_licensing_issue requestBody: content: application/json: example: product: engine signals: machine_id: "\u2026" os: linux schema: $ref: '#/components/schemas/licensing.IssueRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/licensing.IssueResponse' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Issue mints a signed license token for a product the caller's org already pays for. tags: - licensing x-app: licensing x-public: true x-tool: true /v1/licensing/jwks: get: description: "Pubkey publishes the Ed25519 PUBLIC verification key, at both /pubkey and\n/jwks.\n\nThis is the only\ \ public-safe surface here and the reason the whole scheme\nworks offline: the engine embeds or fetches this key once\ \ and then verifies\nevery license itself, with no call home per launch. The private half never\nenters this process\ \ \u2014 it lives in the KMS \u2014 so nothing served here is a\nsecret. `provider` names the KMS holding that half;\ \ `\"local\"` means a\ndevelopment key, and a token signed by one is not a production credential." operationId: get_licensing_jwks responses: '200': content: application/json: schema: $ref: '#/components/schemas/licensing.PubkeyView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Pubkey publishes the Ed25519 PUBLIC verification key, at both /pubkey and /jwks. tags: - licensing x-app: licensing x-public: true x-tool: true /v1/licensing/pubkey: get: description: "Pubkey publishes the Ed25519 PUBLIC verification key, at both /pubkey and\n/jwks.\n\nThis is the only\ \ public-safe surface here and the reason the whole scheme\nworks offline: the engine embeds or fetches this key once\ \ and then verifies\nevery license itself, with no call home per launch. The private half never\nenters this process\ \ \u2014 it lives in the KMS \u2014 so nothing served here is a\nsecret. `provider` names the KMS holding that half;\ \ `\"local\"` means a\ndevelopment key, and a token signed by one is not a production credential." operationId: get_licensing_pubkey responses: '200': content: application/json: schema: $ref: '#/components/schemas/licensing.PubkeyView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Pubkey publishes the Ed25519 PUBLIC verification key, at both /pubkey and /jwks. tags: - licensing x-app: licensing x-public: true x-tool: true /v1/licensing/releases: get: description: 'Lists the signed binary releases this deployment can serve. Metadata only, and no download URL: the artifact is behind GET /v1/licensing/download/{release}, which is gated on a valid license token. Knowing that a release exists is not permission to run it, which is why this list needs no license of its own.' operationId: get_licensing_releases responses: '200': content: application/json: schema: $ref: '#/components/schemas/licensing.ReleaseList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the signed binary releases this deployment can serve. tags: - licensing x-app: licensing x-public: true x-tool: true post: description: "Publishes a signed binary release, answering 201 Created.\n\nOutside dev a release MUST carry its cosign\ \ signature: this is how a binary\nbecomes downloadable, so accepting an unsigned one would let an unverifiable\n\ artifact into the distribution path. Org-admin only \u2014 publishing is an\noperator action, not something a licensee\ \ does." operationId: post_licensing_releases requestBody: content: application/json: example: artifact_ref: "s3://\u2026" cosign_signature: "MEUCIQ\u2026" id: engine-1.4.0-linux-arm64 platform: linux/arm64 product: engine version: 1.4.0 schema: $ref: '#/components/schemas/licensing.Release' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/licensing.Release' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Publishes a signed binary release, answering 201 Created. tags: - licensing x-app: licensing x-public: true x-tool: true /v1/licensing/releases/{release}: get: description: "Reads one release's metadata: its product, version, platform and the\ncosign material a client verifies\ \ the binary against.\n\nAn unknown id is 404. Like the list, this is metadata only \u2014 the bytes are\nbehind the\ \ license-gated download." operationId: get_licensing_releases_by_release parameters: - in: path name: release required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/licensing.Release' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Reads one release''s metadata: its product, version, platform and the cosign material a client verifies the binary against.' tags: - licensing x-app: licensing x-public: true x-tool: true /v1/licensing/revoke: post: description: "Revoke turns off tokens that have already been issued.\n\nA signed token cannot be un-signed, so revocation\ \ is the only way to withdraw\none: this appends an entry that verify and the license-gated download both\nconsult.\ \ It is a POST rather than a DELETE because it APPENDS a durable,\nattributed record \u2014 the entry names the admin\ \ who recorded it and when \u2014\nrather than removing one.\n\nOrg-admin only. Scope it as narrowly as the incident\ \ allows: \"nonce\" for one\nleaked token, \"holder\" for one compromised account, \"fingerprint\" for one\nstolen\ \ machine, \"release\" when a whole build is bad." operationId: post_licensing_revoke requestBody: content: application/json: example: reason: leaked in a public gist scope: nonce value: "9f2c\u2026" schema: $ref: '#/components/schemas/licensing.RevokeRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/licensing.RevokeResponse' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Revoke turns off tokens that have already been issued. tags: - licensing x-app: licensing x-public: true x-tool: true /v1/licensing/verify: post: description: "Verify checks a license token online: signature, schema, expiry, app_id and\nthe revocation list.\n\n\ It is UNAUTHENTICATED and always answers 200 \u2014 a bad token is `valid:false`\nwith a reason rather than an error\ \ status, because \"is this token good\" is a\nquestion anyone may ask about a credential they already hold and the\ \ answer is\nthe same either way. It is also OPTIONAL: the engine verifies OFFLINE against\nthe published public key\ \ (GET /v1/licensing/pubkey) and needs this endpoint\nonly to learn about revocation, so an outage here never stops\ \ a paid customer\nworking." operationId: post_licensing_verify requestBody: content: application/json: example: token: "eyJ2Ijox\u2026.Ab3\u2026" schema: $ref: '#/components/schemas/licensing.VerifyRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/licensing.VerifyResponse' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Verify checks a license token online: signature, schema, expiry, app_id and the revocation list.' tags: - licensing x-app: licensing x-public: true x-tool: true /v1/link: get: description: "Lists your linked accounts and the devices they sit on.\n\nIt answers the caller's own links plus a devices\ \ projection of the same rows\nfolded per machine \u2014 the cross-machine \"AI Providers / Accounts\" view. A\ndevice\ \ is a projection, not a stored entity: its labels come from its\nmost-recently-seen account, so there is no device\ \ to create and none to\ngarbage-collect. Revoked links are INCLUDED rather than dropped, because a\nlogged-out account\ \ keeps its usage history and audit trail. Scoped to the\ncaller: a validated principal and a non-empty org, else\ \ 403." operationId: get_link responses: '200': content: application/json: schema: $ref: '#/components/schemas/link.linkList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists your linked accounts and the devices they sit on. tags: - link x-app: link x-public: true x-tool: true post: description: "Registers a signed-in AI provider account on a machine.\n\nIt records that a developer has signed into\ \ one provider account on one\nmachine \u2014 a Claude Max or ChatGPT Plus subscription, a Hanzo key, a raw\nprovider\ \ key \u2014 and answers 201 with the stored link. Re-reporting the same\n(machine, provider, account) UPDATES that\ \ link rather than creating a second,\nso a collector may call this on every heartbeat. machine and provider are\n\ required (400 otherwise), as is a valid kind, and every field is\nlength-bounded. Scoped to the caller: a validated\ \ principal and a non-empty\norg, else 403, so a caller writes only their OWN accounts within their own org." operationId: post_link requestBody: content: application/json: schema: $ref: '#/components/schemas/link.enrollReq' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/link.linkView' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Registers a signed-in AI provider account on a machine. tags: - link x-app: link x-public: true x-tool: true /v1/link/devices/{machine}: get: description: "Shows one machine: its accounts, usage and live sessions.\n\nIt answers one device \u2014 its host and\ \ OS labels, every account the caller has\nsigned in on that machine with its latest usage, and how many agent sessions\n\ the caller currently has running on it. The device labels come from the\nmost-recently-seen account, since a device\ \ is a projection of its links rather\nthan a row of its own. A machine with none of the caller's accounts is 404,\n\ which is also the answer when the machine belongs to someone else \u2014 the scope\nmakes the two indistinguishable,\ \ deliberately. The session count reports 0\nwhere the agent plane is not mounted rather than failing the read." operationId: get_link_devices_by_machine parameters: - description: 'Machine is the machine to act on, from the path. It is scoped to the caller, so a machine with none of the caller''s accounts is a 404.' in: path name: machine required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/link.deviceView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Shows one machine: its accounts, usage and live sessions.' tags: - link x-app: link x-public: true x-tool: true /v1/link/devices/{machine}/revoke: post: description: "Logs out every account on one machine and stops its sessions.\n\nIt revokes every one of the caller's\ \ accounts on one machine and stops the\nagent sessions they were running, answering with how many of each. This is\ \ the\n\"I lost that laptop\" button. Revoked links are RETAINED, not deleted, so usage\nhistory and the audit trail\ \ survive a log-out \u2014 the rows come back in the\nresponse with their new status. The session stop reaches only\ \ the REVOKING\nuser's own sessions, so a shared machine name can never be used to stop a\nco-tenant's work, and a\ \ stop that fails does not fail the revoke: the revoked\nrow is the durable truth and the count then honestly reports\ \ fewer. A machine\nwith nothing left to revoke is 404." operationId: post_link_devices_by_machine_revoke parameters: - description: 'Machine is the machine to act on, from the path. It is scoped to the caller, so a machine with none of the caller''s accounts is a 404.' in: path name: machine required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/link.revokeResp' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Logs out every account on one machine and stops its sessions. tags: - link x-app: link x-public: true x-tool: true /v1/link/route: get: description: "Gets the failover order across your linked accounts.\n\nIt answers an ordered redundancy plan over the\ \ caller's LINKED (not revoked)\naccounts: each candidate with its remaining rate-limit headroom, whether it is\n\ routable right now, how it BILLS (plan or commerce), and a reason when it is\nnot \u2014 plus the primary to try first.\ \ It is what lets a router fail over from\none subscription to another and fall back to the metered API as the\nalways-available\ \ backstop, knowing the cost consequence before it dials.\n\nIt is POLICY, not execution: the plan is computed purely\ \ from the usage\nsnapshots already in the registry, never by probing a provider, so it is a\ntotal function of the\ \ links and costs nothing to ask for. Actually dialing,\ndetecting a live 429 and advancing to the next candidate\ \ belongs to the\ncaller. A link with no snapshot counts as full headroom." operationId: get_link_route responses: '200': content: application/json: schema: $ref: '#/components/schemas/link.RoutePlan' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Gets the failover order across your linked accounts. tags: - link x-app: link x-public: true x-tool: true /v1/link/usage: get: description: "Shows one provider account's own usage dashboard.\n\nIt answers the time series for a SINGLE provider\ \ account \u2014 the windows in\nrange plus the currently-open ones \u2014 as that provider's own meter reported it:\n\ \"my plan is 47% through its 6h window, resets at 14:20\". current is the newest\ninstance of each lane (the headline);\ \ windows is the history behind it, both\ncomputed from ONE deduped read. provider is required; an unknown window\ \ class\nor range is 400, never a quiet fallback to a different one. When no series is\navailable the response is\ \ a 200 with available:false and empty lists \u2014 an\nhonest \"we have no data\", which is a different claim from\ \ zero usage." operationId: get_link_usage parameters: - description: Provider is the provider whose meter to read. Required. in: query name: provider required: false schema: type: string - description: Account narrows to one account when a user has several with the provider. in: query name: account required: false schema: type: string - description: 'Window selects a window class: 6h, day, week or month. Empty reads all.' in: query name: window required: false schema: type: string - description: 'Range is the period, one of 1h, 24h, 7d or 30d; empty means 24h, and an unknown label is 400, never a quiet fallback.' in: query name: range required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/link.boardResp' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Shows one provider account's own usage dashboard. tags: - link x-app: link x-public: true x-tool: true post: description: "Reports usage samples from the device collector.\n\nIt ingests a batch of usage samples and answers with\ \ how many were accepted,\nwhether history was durably stored, and the links they refreshed. A report\nalso REFRESHES\ \ one link per distinct (machine, provider, account) it names, so\na running collector keeps the accounts overview\ \ current without a separate\nregistration call.\n\nA caller can only ever report for THEMSELVES: org and subject\ \ come from the\nvalidated bearer, never from the body, so no sample can be attributed to\nanother user or tenant.\ \ History is FAIL-SOFT and stored says which happened \u2014\na warehouse outage still accepts the report and refreshes\ \ the links rather\nthan failing the device, and answers 202 either way. Send either one sample\ninline or up to 256\ \ in samples; an empty batch or an over-long one is 400, as\nis a provider, window class or kind outside the closed\ \ vocabulary \u2014 an\nunrecognized window is refused rather than rewritten, because a silently\nreclassified sample\ \ would fill a dashboard with a class nobody reported." operationId: post_link_usage requestBody: content: application/json: schema: $ref: '#/components/schemas/link.ingestReq' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/link.ingestResp' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports usage samples from the device collector. tags: - link x-app: link x-public: true x-tool: true /v1/link/usage/accounts: get: description: "Breaks down what the gateway routed through each of your accounts.\n\nIt answers one row per linked account\ \ the GATEWAY actually routed through,\nplus their total \u2014 requests, prompt and completion tokens, and cost.\ \ This is\nthe routed ledger, the read twin of the counter the router writes, and it is\ndistinct from both of its\ \ neighbours: not the device collector's plan\nsnapshots, and not the org money ledger. The source and scope fields\ \ on the\nresponse say so on every payload. The same shape answers in the billing\nnamespace, from one shaping function,\ \ so the two mounts cannot drift." operationId: get_link_usage_accounts responses: '200': content: application/json: schema: $ref: '#/components/schemas/link.AccountsUsage' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Breaks down what the gateway routed through each of your accounts. tags: - link x-app: link x-public: true x-tool: true /v1/link/usage/summary: get: description: "Shows plan consumption and Hanzo spend side by side.\n\nIt answers the global usage board over one window:\ \ the caller's own linked\naccounts, metered from each provider's own login, alongside their org's\nHanzo-routed inference.\ \ These come from different ledgers and mean different\nthings, so every row is LABELLED by source, by scope and by\ \ availability, and\nTHE TWO ARE NEVER SUMMED \u2014 a plan's percentage is not money, and a provider's\nown spend\ \ is not a Hanzo charge. The rows sit side by side and say what they\nare.\n\nOne resolver fixes the window for both\ \ halves, so the two sets always cover\nthe same period. range is one of 1h, 24h, 7d or 30d and defaults to 24h;\n\ anything else is 400 rather than a silent substitution. A ledger that cannot\nanswer reports available:false instead\ \ of a zero that would read as \"no usage\"." operationId: get_link_usage_summary parameters: - description: 'Range is the period, one of 1h, 24h, 7d or 30d; empty means 24h, and an unknown label is 400, never a silent substitution.' in: query name: range required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/link.summaryResp' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Shows plan consumption and Hanzo spend side by side. tags: - link x-app: link x-public: true x-tool: true /v1/link/{id}: delete: description: "Logs out one account and stops the sessions it was running.\n\nIt revokes a single linked account and\ \ stops the agent sessions that ran under\nit, answering with the revoked row and how many sessions stopped. The link\ \ is\nRETAINED with a revoked status rather than deleted, so its usage history and\nthe audit trail survive the log-out\ \ \u2014 which also means a revoked account still\nappears in the list, and is excluded from the route plan rather\ \ than absent\nfrom it. The session stop is narrowed to the revoking user's own sessions on\nthat device, provider\ \ and account, and a stop that fails does not fail the\nrevoke: the revoked row is the durable truth. An id that does\ \ not exist, or\nbelongs to another user or org, is the same 404." operationId: delete_link_by_id parameters: - description: 'ID is the link to act on, from the path. It is scoped to the caller, so another user''s or org''s id is a 404.' in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/link.revokeResp' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Logs out one account and stops the sessions it was running. tags: - link x-app: link x-public: true x-tool: true get: description: "Reads one linked account.\n\nIt answers a single link \u2014 its device, provider, account, plan, how\ \ it bills,\nits status and its latest usage snapshot. An id that does not exist, or\nbelongs to another user or org,\ \ is the same 404: the scope is a bound\npredicate on the read, so a wrong id and a foreign id are indistinguishable\n\ and neither confirms the other's existence. The static paths on this\ncollection \u2014 route, usage, devices \u2014\ \ register before this one and win\nfirst-match, so a link whose id collided with one of those words could not be\n\ addressed here." operationId: get_link_by_id parameters: - description: 'ID is the link to act on, from the path. It is scoped to the caller, so another user''s or org''s id is a 404.' in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/link.linkView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads one linked account. tags: - link x-app: link x-public: true x-tool: true /v1/lsp/complete: post: description: 'Offers the candidates a language server has at a position, typed and resolved through the repository''s dependencies rather than guessed from text.' operationId: post_lsp_complete requestBody: content: application/json: example: character: 18 line: 120 path: apps/lsp/lsp.go repo: cloud schema: $ref: '#/components/schemas/lsp.Query' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/lsp.Answer' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Offers the candidates a language server has at a position, typed and resolved through the repository's dependencies rather than guessed from text. tags: - lsp x-app: lsp x-public: true x-tool: true /v1/lsp/diagnostics: post: description: "Reports every problem the language server finds in one file \u2014\ncompile errors, type errors and lints,\ \ each with its span and its severity (1\nerror, 2 warning, 3 information, 4 hint). The position is ignored." operationId: post_lsp_diagnostics requestBody: content: application/json: example: path: apps/lsp/lsp.go repo: cloud schema: $ref: '#/components/schemas/lsp.Query' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/lsp.Answer' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Reports every problem the language server finds in one file \u2014 compile errors, type errors and lints,\ \ each with its span and its severity (1 error, 2 warning, 3 information, 4 hint)." tags: - lsp x-app: lsp x-public: true x-tool: true /v1/lsp/hover: post: description: 'Renders the type and documentation of the symbol at a position, as the language server itself renders it. Positions are the LSP''s: line and character are 0-BASED and character counts UTF-16 code units, so an editor''s 1-based line must have 1 subtracted before it is sent. The repository is named by slug and is always one in the caller''s own org; rev pins a branch, tag or commit sha, and empty means the default branch.' operationId: post_lsp_hover requestBody: content: application/json: example: character: 18 line: 120 path: apps/lsp/lsp.go repo: cloud schema: $ref: '#/components/schemas/lsp.Query' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/lsp.Answer' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Renders the type and documentation of the symbol at a position, as the language server itself renders it. tags: - lsp x-app: lsp x-public: true x-tool: true /v1/lsp/locate: post: description: "Finds where a symbol lives: its definition, its references, its type or\nits implementations, chosen by\ \ relation (definition, reference, type,\nimplementation \u2014 empty means definition).\n\nIt resolves THROUGH dependencies.\ \ An answer whose external flag is set left the\nrepository, and its path is then the module coordinate it landed\ \ in \u2014 which is\nthe question a static index cannot answer and this service exists for." operationId: post_lsp_locate requestBody: content: application/json: example: character: 18 line: 120 path: apps/lsp/lsp.go relation: definition repo: cloud schema: $ref: '#/components/schemas/lsp.Query' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/lsp.Answer' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Finds where a symbol lives: its definition, its references, its type or its implementations, chosen by relation\ \ (definition, reference, type, implementation \u2014 empty means definition)." tags: - lsp x-app: lsp x-public: true x-tool: true /v1/lsp/symbols: post: description: "Outlines one file: every declaration in it, with its kind and its span.\nThe position is ignored \u2014\ \ the answer is the whole file." operationId: post_lsp_symbols requestBody: content: application/json: example: path: apps/lsp/lsp.go repo: cloud schema: $ref: '#/components/schemas/lsp.Query' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/lsp.Answer' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Outlines one file: every declaration in it, with its kind and its span.' tags: - lsp x-app: lsp x-public: true x-tool: true /v1/market/chains: get: description: "Answers every chain this deployment can read, what is deployed on each, and what\nits automated market\ \ maker amounts to.\n\nOne call. It reads the chain registry, then every chain's indexer for its figures\nand its\ \ most recent active day, all at once \u2014 where a client doing it itself makes\none registry request and two more\ \ per chain.\n\nTHE ROW IS THE UNIT OF TRUTH. Each carries its own reach, so one indexer being\nunreachable costs\ \ one row its figures and leaves the rest answered. A chain with no\nmarket maker deployed \u2014 the registry names\ \ no factory for it \u2014 answers `read` with\ntotals of nothing, which is a fact about that chain and is not the\ \ same as a chain\nnobody could ask." operationId: get_market_chains responses: '200': content: application/json: schema: $ref: '#/components/schemas/market.Roster' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused security: [] summary: Answers every chain this deployment can read, what is deployed on each, and what its automated market maker amounts to. tags: - market x-app: market x-public: true x-tool: true /v1/market/pools: get: description: 'Answers the automated market makers on one chain: their two tokens, their fee tier, and what has moved through each. The two price fields on a pool are the ratio its own reserves stand at, as the indexer computed them. They are not a price ON either token and not a mark: nothing here derives one, ranks the pools, or names a route through them. A chain with no market maker deployed answers `read` with no pools. That is the chain''s real condition, and it is deliberately not the same answer as an indexer that could not be asked.' operationId: get_market_pools parameters: - description: "Chain is the chain's slug \u2014 `cchain`, `zoo` \u2014 as `chains` reports it. It is\nthe indexer's\ \ word for the chain and NOT the chain id: `96369`, `C` and\n`c-chain` all name nothing." in: query name: chain required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/market.Pools' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused security: [] summary: 'Answers the automated market makers on one chain: their two tokens, their fee tier, and what has moved through each.' tags: - market x-app: market x-public: true x-tool: true /v1/market/survey: get: description: "Answers which of the four settlement precompiles carry code on one chain.\n\nAn address with no code answers\ \ a call with empty data rather than an error, so\n\"this chain has no view precompile\" and \"this market was never\ \ opened\" reach a\ncaller as the same silence \u2014 and only the second is a fact about a market. This\nsays which\ \ it is, by asking the node for the code at each address.\n\nIt reads presence and nothing else. No market, no quote,\ \ no depth and no order is\nrequested here, and `eth_getCode` is the only method this operation ever sends." operationId: get_market_survey parameters: - description: "Chain is the chain's slug \u2014 `cchain`, `zoo` \u2014 as `chains` reports it. It is\nthe indexer's\ \ word for the chain and NOT the chain id: `96369`, `C` and\n`c-chain` all name nothing." in: query name: chain required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/market.Survey' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused security: [] summary: Answers which of the four settlement precompiles carry code on one chain. tags: - market x-app: market x-public: true x-tool: true /v1/market/token: get: description: "Answers one token's daily history \u2014 open, high, low, close, price and volume per\nUTC day, oldest\ \ first.\n\nEvery figure is the indexer's own arithmetic, passed through as the decimal string\nit computed. Nothing\ \ here rounds one, converts one, or fills a gap: a day the\nindexer holds no figure for arrives with that field absent,\ \ which says \"not\nindexed\" where a zero would say \"worth nothing\"." operationId: get_market_token parameters: - in: query name: chain required: false schema: type: string - description: At is the token's contract address. in: query name: at required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/market.History' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused security: [] summary: "Answers one token's daily history \u2014 open, high, low, close, price and volume per UTC day, oldest first." tags: - market x-app: market x-public: true x-tool: true /v1/market/tokens: get: description: 'Answers the tokens one chain''s indexer has seen, with the decimals a caller needs to read any amount of one correctly. This is what the indexer INGESTED, which is not the same as what exists on the chain: a token nothing has traded has no row here, and this is not a registry of what is permitted or listed.' operationId: get_market_tokens parameters: - description: "Chain is the chain's slug \u2014 `cchain`, `zoo` \u2014 as `chains` reports it. It is\nthe indexer's\ \ word for the chain and NOT the chain id: `96369`, `C` and\n`c-chain` all name nothing." in: query name: chain required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/market.Tokens' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused security: [] summary: Answers the tokens one chain's indexer has seen, with the decimals a caller needs to read any amount of one correctly. tags: - market x-app: market x-public: true x-tool: true /v1/marketing/audiences: get: description: Returns the org's saved audiences, most recently updated first. operationId: get_marketing_audiences parameters: - description: Limit caps the rows returned; 0 means 200 and nothing above 1000 is honoured. example: 50 in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.AudienceList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the org's saved audiences, most recently updated first. tags: - marketing x-app: marketing x-public: true x-tool: true post: description: "Saves a cohort filter for the caller's org. Name is required.\nOmitting event saves the WHOLE-ORG audience\ \ \u2014 every mailable customer \u2014 which\nneeds no analytics warehouse; naming one narrows that roster to the\ \ customers\nwho fired it within windowDays." operationId: post_marketing_audiences requestBody: content: application/json: example: event: model.invoked name: Model users, last 30d windowDays: 30 schema: $ref: '#/components/schemas/marketing.Audience' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.Audience' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Saves a cohort filter for the caller's org. tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/audiences/{id}: delete: description: "Removes one of the caller org's audiences and answers 204. It\ndeletes the saved filter only \u2014 no\ \ customer, event or enrollment is touched." operationId: delete_marketing_audiences_by_id parameters: - description: ID is the audience id from the path, as returned by create. example: aud_4c1e9b7a2d6f0538e4a7c9b1d3f5027a in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one of the caller org's audiences and answers 204. tags: - marketing x-app: marketing x-public: true x-tool: true get: description: 'Returns one of the caller org''s saved audiences. An audience belonging to another org reads as not found.' operationId: get_marketing_audiences_by_id parameters: - description: ID is the audience id from the path, as returned by create. example: aud_4c1e9b7a2d6f0538e4a7c9b1d3f5027a in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.Audience' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one of the caller org's saved audiences. tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/audiences/{id}/preview: get: description: "Evaluates the cohort LIVE \u2014 the same resolution an enrollment\nwould run \u2014 and reports how big\ \ it is and how many real mailboxes it reaches.\nIt is the honest answer to \"is this send worth making\": a cohort\ \ of 500 that\nmails 3 says so, in deliverable and unmatched. Nothing is sent." operationId: get_marketing_audiences_by_id_preview parameters: - description: ID is the audience id from the path, as returned by create. example: aud_4c1e9b7a2d6f0538e4a7c9b1d3f5027a in: path name: id required: true schema: type: string responses: '200': content: application/json: example: available: true count: 500 deliverable: 3 sample: - u_1 - u_2 source: event.fact unmatched: 497 schema: $ref: '#/components/schemas/marketing.AudiencePreview' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Evaluates the cohort LIVE \u2014 the same resolution an enrollment would run \u2014 and reports how big it\ \ is and how many real mailboxes it reaches." tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/calendar: get: description: 'Returns the org''s calendar, latest scheduled first, optionally narrowed to one status.' operationId: get_marketing_calendar parameters: - description: 'Status keeps only posts in that state (draft, scheduled, published, failed, canceled). Empty means every post.' example: scheduled in: query name: status required: false schema: type: string - description: Limit caps the rows returned; 0 means 200 and nothing above 1000 is honoured. example: 50 in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.PostList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the org's calendar, latest scheduled first, optionally narrowed to one status. tags: - marketing x-app: marketing x-public: true x-tool: true post: description: "Adds a post to the content calendar. Channel and body are\nrequired. A scheduledAt in the future makes\ \ the post \"scheduled\" and the\ndurable sweep publishes it when it comes due \u2014 claimed once, so a post\npublishes\ \ at most once; without one it stays a draft." operationId: post_marketing_calendar requestBody: content: application/json: example: body: Hanzo Cloud is live. channel: x scheduledAt: 1780000000 title: Launch day schema: $ref: '#/components/schemas/marketing.CalendarPost' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.CalendarPost' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Adds a post to the content calendar. tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/calendar/{id}: delete: description: "Removes one of the caller org's posts and answers 204. A\npost already published is deleted from the calendar\ \ only \u2014 nothing is\nretracted from the network it went out on." operationId: delete_marketing_calendar_by_id parameters: - description: ID is the post id from the path, as returned by create. example: cal_1d7f3b9e5a2c8046f1b3d5a7c9e02468 in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one of the caller org's posts and answers 204. tags: - marketing x-app: marketing x-public: true x-tool: true get: description: 'Returns one of the caller org''s posts, including the exact error behind a failed publish. A post belonging to another org reads as not found.' operationId: get_marketing_calendar_by_id parameters: - description: ID is the post id from the path, as returned by create. example: cal_1d7f3b9e5a2c8046f1b3d5a7c9e02468 in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.CalendarPost' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one of the caller org's posts, including the exact error behind a failed publish. tags: - marketing x-app: marketing x-public: true x-tool: true put: description: "Replaces a post's editable fields. It is a full write, not\na patch, and it RESETS the lifecycle from\ \ the schedule: a scheduledAt makes\nthe post \"scheduled\" again and none makes it a draft \u2014 so editing a failed\n\ post requeues it rather than leaving it stuck." operationId: put_marketing_calendar_by_id parameters: - description: ID is the server-assigned post id ("cal_" + 128 random bits). in: path name: id required: true schema: type: string requestBody: content: application/json: example: body: "Hanzo Cloud is live \u2014 try it free." channel: x scheduledAt: 1780003600 title: Launch day schema: $ref: '#/components/schemas/marketing.CalendarPost' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.CalendarPost' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Replaces a post's editable fields. tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/campaigns: get: description: 'Returns the org''s campaigns, most recently updated first, optionally narrowed to one lifecycle status.' operationId: get_marketing_campaigns parameters: - description: 'Status keeps only campaigns in that lifecycle state (draft, scheduled, active, paused, completed). Empty means every campaign.' example: active in: query name: status required: false schema: type: string - description: Limit caps the rows returned; 0 means 200 and nothing above 1000 is honoured. example: 25 in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.CampaignList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the org's campaigns, most recently updated first, optionally narrowed to one lifecycle status. tags: - marketing x-app: marketing x-public: true x-tool: true post: description: "Registers a campaign in the caller's org. Name is required;\nchannel defaults to email and status to draft,\ \ and a future scheduledAt with\nno explicit status makes the campaign \"scheduled\". Budget and spend are cents\n\ and are clamped to >= 0. The id, createdAt and updatedAt of the input are\nignored \u2014 the server assigns them." operationId: post_marketing_campaigns requestBody: content: application/json: example: budget: 50000 channel: meta name: Spring Launch objective: signups scheduledAt: 1780000000 schema: $ref: '#/components/schemas/marketing.Campaign' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.Campaign' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Registers a campaign in the caller's org. tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/campaigns/{id}: delete: description: 'Removes one of the caller org''s campaigns and answers 204. A campaign belonging to another org reads as not found and is left untouched.' operationId: delete_marketing_campaigns_by_id parameters: - description: ID is the campaign id from the path, as returned by create. example: camp_9f2a1c7d4e8b0a6f3d2c5b1e7a9f4c60 in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one of the caller org's campaigns and answers 204. tags: - marketing x-app: marketing x-public: true x-tool: true get: description: 'Returns one of the caller org''s campaigns. A campaign belonging to another org reads as not found.' operationId: get_marketing_campaigns_by_id parameters: - description: ID is the campaign id from the path, as returned by create. example: camp_9f2a1c7d4e8b0a6f3d2c5b1e7a9f4c60 in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.Campaign' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one of the caller org's campaigns. tags: - marketing x-app: marketing x-public: true x-tool: true put: description: "Replaces a campaign's editable fields. It is a full write, not\na patch: every field takes the value in\ \ the body, and an omitted one is\ncleared. The id comes from the path \u2014 the body cannot retarget another\ncampaign\ \ \u2014 and createdAt is never rewritten." operationId: put_marketing_campaigns_by_id parameters: - description: ID is the server-assigned campaign id ("camp_" + 128 random bits). in: path name: id required: true schema: type: string requestBody: content: application/json: example: budget: 50000 channel: meta name: Spring Launch objective: signups spend: 12500 status: active schema: $ref: '#/components/schemas/marketing.Campaign' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.Campaign' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Replaces a campaign's editable fields. tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/campaigns/{id}/schedule: post: description: 'Sets a campaign''s send time and moves it to "scheduled". A scheduledAt of 0 clears the schedule and returns it to "draft".' operationId: post_marketing_campaigns_by_id_schedule parameters: - description: ID is the campaign id from the path. example: camp_9f2a1c7d4e8b0a6f3d2c5b1e7a9f4c60 in: path name: id required: true schema: type: string requestBody: content: application/json: example: id: camp_9f2a1c7d4e8b0a6f3d2c5b1e7a9f4c60 scheduledAt: 1780000000 schema: $ref: '#/components/schemas/marketing.ScheduleInput' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.Campaign' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Sets a campaign's send time and moves it to "scheduled". tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/leads: post: description: "Files a sales inquiry as a new lead in the deployment's own CRM and states\nit on the event plane. No\ \ account is needed and none is read: every lead lands\nin the brand's org. Answers 201 with an opaque reference.\n\ \nA brand org without the CRM installed answers 503, a body over 16 KiB answers\n413, and a missing or malformed email\ \ answers 400 \u2014 in that order." operationId: post_marketing_leads requestBody: content: application/json: schema: $ref: '#/components/schemas/marketing.LeadIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/marketing.Lead' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Files a sales inquiry as a new lead in the deployment's own CRM and states it on the event plane. tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/promos: get: description: "Returns every promo the deployment offers with its live counters:\nhow many orgs have redeemed it and\ \ how many redemptions remain under the cap.\nThe promos are fleet-wide, not per-org \u2014 only the counters move." operationId: get_marketing_promos responses: '200': content: application/json: example: data: - promo: active: true code: first1000 maxRedemptions: 1000 percentOff: 90 redeemed: 137 remaining: 863 schema: $ref: '#/components/schemas/marketing.PromoList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns every promo the deployment offers with its live counters: how many orgs have redeemed it and how many redemptions remain under the cap.' tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/promos/{code}/eligibility: get: description: 'Prices a promo against a plan and seat count. It is PURE: nothing is redeemed, credited or counted, so it is safe to call from a pricing page on every keystroke. An inactive promo or an exhausted cap quotes ineligible with the reason rather than erroring.' operationId: get_marketing_promos_by_code_eligibility parameters: - description: Code is the promo code from the path. example: first1000 in: path name: code required: true schema: type: string - description: 'Plan is the plan being priced: pro, max or team. Anything else (including the free Developer plan) has no list price and so nothing to discount.' example: team in: query name: plan required: false schema: type: string - description: 'Seats is the Team seat count; 0 means 1, and it is ignored for the single-seat plans.' example: 12 in: query name: seats required: false schema: format: int64 type: integer responses: '200': content: application/json: example: chargeCents: 7500 code: first1000 discountCents: 22500 eligible: true listCents: 2500 plan: team remaining: 863 seats: 12 schema: $ref: '#/components/schemas/marketing.Quote' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Prices a promo against a plan and seat count. tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/promos/{code}/redeem: post: description: "Records the caller org's claim on a promo. NOTHING IS CREDITED:\nthe redemption is a row, and credit into\ \ an org is an admin decision made on\nthe admin surface against an auditable ledger.\n\nThe plan is DERIVED from\ \ the org's live ACTIVE/TRIALING paid subscription and\ncan never be named by the caller \u2014 an org with no qualifying\ \ subscription is\nrefused, and so is one whose subscription cannot be read. The seat count is\nthe single-seat floor\ \ (claimSeats), so the recorded figure has no input that\ncan inflate it.\n\nGuards run under one lock so the cap\ \ cannot be raced past: the fleet-wide\nredemption cap, one redemption per org, one per payment instrument (REQUIRED),\n\ and the per-redemption ceiling.\n\nIt is IDEMPOTENT: an org that already redeemed gets its original redemption\nback\ \ with alreadyRedeemed true." operationId: post_marketing_promos_by_code_redeem parameters: - description: Code is the promo code from the path. example: first1000 in: path name: code required: true schema: type: string requestBody: content: application/json: example: code: first1000 instrument: pm_1QxYz2AbCdEf schema: $ref: '#/components/schemas/marketing.RedeemInput' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.RedeemResult' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records the caller org's claim on a promo. tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/promos/{code}/redemption: get: description: "Returns the caller org's OWN redemption of a promo \u2014 an\norg-scoped read, so it can never surface\ \ another tenant's. Not found when this\norg has not redeemed it." operationId: get_marketing_promos_by_code_redemption parameters: - description: Code is the promo code from the path, e.g. "first1000". example: first1000 in: path name: code required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.Redemption' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the caller org's OWN redemption of a promo \u2014 an org-scoped read, so it can never surface another\ \ tenant's." tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/sequences: get: description: Returns the org's drip sequences, most recently updated first. operationId: get_marketing_sequences parameters: - description: Limit caps the rows returned; 0 means 200 and nothing above 1000 is honoured. example: 50 in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.SequenceList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the org's drip sequences, most recently updated first. tags: - marketing x-app: marketing x-public: true x-tool: true post: description: "Registers a drip sequence in the caller's org. Name is\nrequired; status defaults to draft, and a sequence\ \ must be ACTIVE before it\nwill accept enrollments. The id, createdAt and updatedAt of the input are\nignored \u2014\ \ the server assigns them." operationId: post_marketing_sequences requestBody: content: application/json: example: name: Trial onboarding status: draft schema: $ref: '#/components/schemas/marketing.Sequence' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.Sequence' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Registers a drip sequence in the caller's org. tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/sequences/{id}: get: description: 'Returns one of the caller org''s sequences together with its steps in send order. A sequence belonging to another org reads as not found.' operationId: get_marketing_sequences_by_id parameters: - description: ID is the sequence id from the path, as returned by create. example: seq_7b3e5a1c9d024f68b0a3e7c5d9f1a248 in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.SequenceView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one of the caller org's sequences together with its steps in send order. tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/sequences/{id}/enroll: post: description: "Adds one contact or a whole audience to a sequence and schedules the\nfirst step for each. The sequence\ \ must be ACTIVE (a draft sends nothing), and\nthe request must name exactly one of address or audienceId.\n\nEnrolling\ \ is ALL this does: the message itself is sent later by the drip\nengine, through the suppression gate, so an opted-out\ \ customer can be enrolled\nhere and still never be mailed. Re-posting is safe \u2014 an address this sequence\nalready\ \ took is counted in alreadyEnrolled and never double-dripped \u2014 which is\nwhat makes retrying a partially-applied\ \ announcement a resume rather than a\nsecond send." operationId: post_marketing_sequences_by_id_enroll parameters: - description: ID is the sequence id from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: audienceId: aud_4c1e9b7a2d6f0538e4a7c9b1d3f5027a channel: email schema: $ref: '#/components/schemas/marketing.EnrollInput' required: true responses: '200': content: application/json: example: alreadyEnrolled: 3 enrolled: 409 resolved: 412 schema: $ref: '#/components/schemas/marketing.EnrollResult' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Adds one contact or a whole audience to a sequence and schedules the first step for each. tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/sequences/{id}/enrollments: get: description: 'Returns who is walking one sequence, most recently enrolled first, with each walk''s current step and next due time.' operationId: get_marketing_sequences_by_id_enrollments parameters: - description: ID is the sequence id from the path. example: seq_7b3e5a1c9d024f68b0a3e7c5d9f1a248 in: path name: id required: true schema: type: string - description: Limit caps the rows returned; 0 means 200 and nothing above 1000 is honoured. example: 100 in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.EnrollmentList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns who is walking one sequence, most recently enrolled first, with each walk's current step and next due time. tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/sequences/{id}/enrollments/{eid}/cancel: post: description: "Stops one walk mid-sequence and answers 204: no further step\nis sent, and steps already delivered are\ \ not recalled. Only an ACTIVE\nenrollment can be canceled \u2014 one already completed or canceled reads as not\n\ found." operationId: post_marketing_sequences_by_id_enrollments_by_eid_cancel parameters: - description: ID is the sequence id from the path. example: seq_7b3e5a1c9d024f68b0a3e7c5d9f1a248 in: path name: id required: true schema: type: string - description: 'EID is the enrollment id from the path, as returned by a single-address enroll.' example: enr_2a8d6f0b4c1e9375a0d2f6b8c4e19f73 in: path name: eid required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Stops one walk mid-sequence and answers 204: no further step is sent, and steps already delivered are not recalled.' tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/sequences/{id}/status: post: description: "Flips draft/active/archived \u2014 the activation gate for\nsending, since only an active sequence accepts\ \ enrollments. It does not touch\nenrollments already walking: archiving stops new ones, not in-flight ones." operationId: post_marketing_sequences_by_id_status parameters: - description: ID is the sequence id from the path. example: seq_7b3e5a1c9d024f68b0a3e7c5d9f1a248 in: path name: id required: true schema: type: string requestBody: content: application/json: example: id: seq_7b3e5a1c9d024f68b0a3e7c5d9f1a248 status: active schema: $ref: '#/components/schemas/marketing.SequenceStatus' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.SequenceStatus' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Flips draft/active/archived \u2014 the activation gate for sending, since only an active sequence accepts\ \ enrollments." tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/sequences/{id}/steps: get: description: Returns one sequence's steps in send order. operationId: get_marketing_sequences_by_id_steps parameters: - description: ID is the sequence id from the path, as returned by create. example: seq_7b3e5a1c9d024f68b0a3e7c5d9f1a248 in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.StepList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one sequence's steps in send order. tags: - marketing x-app: marketing x-public: true x-tool: true post: description: "Appends a message to the END of a sequence: the new step's idx is one\npast the last, so steps arrive\ \ in the order they are added. Body is required\nand delaySeconds must be >= 0. Adding a step does not disturb enrollments\n\ already walking \u2014 one that has passed this index simply never sees it." operationId: post_marketing_sequences_by_id_steps parameters: - description: SequenceID is the sequence id from the path (the route's :id). in: path name: id required: true schema: type: string requestBody: content: application/json: example: body: "Here is how to make your first request\u2026" delaySeconds: 86400 subject: 'Day 1: your first model call' schema: $ref: '#/components/schemas/marketing.StepInput' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.Step' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Appends a message to the END of a sequence: the new step''s idx is one past the last, so steps arrive in the order they are added.' tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/summary: get: description: 'Rolls up the caller org''s campaigns: how many there are, how many are active, and the summed budget and spend in cents.' operationId: get_marketing_summary responses: '200': content: application/json: example: active: 3 budget: 500000 campaigns: 12 spend: 128400 schema: $ref: '#/components/schemas/marketing.Summary' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Rolls up the caller org''s campaigns: how many there are, how many are active, and the summed budget and spend in cents.' tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/suppressions: delete: description: 'Re-subscribes an address on one channel and answers 204. An address that is not on the list reads as not found.' operationId: delete_marketing_suppressions parameters: - description: 'Channel is the surface opted out of: email, sms, social, meta, google or tiktok. Empty means email. Opting out of one leaves the others reachable.' example: email in: query name: channel required: false schema: type: string - description: 'Address is the recipient, normalized (lower-cased, trimmed) so an opt-out cannot be slipped past on a case or whitespace difference. Required.' example: person@example.com in: query name: address required: false schema: type: string - description: 'Reason is a free-text note, capped at 1024 bytes. The public one-click endpoint records "one-click unsubscribe".' in: query name: reason required: false schema: type: string - description: CreatedAt is unix seconds, server-assigned. in: query name: createdAt required: false schema: format: int64 type: integer responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Re-subscribes an address on one channel and answers 204. tags: - marketing x-app: marketing x-public: true x-tool: true get: description: "Returns the org's opt-out list, newest first \u2014 everyone the\nsend gate will refuse to deliver to." operationId: get_marketing_suppressions parameters: - description: Limit caps the rows returned; 0 means 200 and nothing above 1000 is honoured. example: 100 in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.SuppressionList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the org's opt-out list, newest first \u2014 everyone the send gate will refuse to deliver to." tags: - marketing x-app: marketing x-public: true x-tool: true post: description: 'Records an opt-out for the org (admin / self-service management). Address is required; channel defaults to email. It is idempotent: re-suppressing the same tuple keeps the original record rather than erroring. From here on the ONE send gate refuses that recipient on that channel.' operationId: post_marketing_suppressions requestBody: content: application/json: example: address: person@example.com channel: email reason: asked support to stop schema: $ref: '#/components/schemas/marketing.Suppression' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketing.Suppression' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records an opt-out for the org (admin / self-service management). tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketing/unsubscribe: get: description: "Is the PUBLIC one-click endpoint (no principal): a recipient\nclicks the signed link in an email footer.\ \ The token binds (org, channel,\naddress), so a caller can only opt OUT exactly the tuple it was minted for \u2014\ \nnever another address and never another org. An invalid token is refused, and\na deployment with no KMS-sealed key\ \ refuses rather than accepting anything." operationId: get_marketing_unsubscribe parameters: - description: Org is the org the link was minted for. example: acme in: query name: org required: false schema: type: string - description: Channel is the surface to opt out of. example: email in: query name: channel required: false schema: type: string - description: Address is the recipient to opt out. example: person@example.com in: query name: address required: false schema: type: string - description: "Token is the HMAC over (org, channel, address). It is the ONLY authority\nhere \u2014 there is no principal\ \ \u2014 so it binds the request to one tuple and\nnothing else." example: "9f2a\u2026" in: query name: token required: false schema: type: string responses: '200': content: application/json: example: address: person@example.com channel: email unsubscribed: true schema: $ref: '#/components/schemas/marketing.Unsubscribed' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Is the PUBLIC one-click endpoint (no principal): a recipient clicks the signed link in an email footer.' tags: - marketing x-app: marketing x-public: true x-tool: true /v1/marketplace: get: description: 'Lists every tool and agent the caller can reach in their own org and project, enriched with any public listing''s title, category and price, and with installed=true on the ones already activated for that scope. It is the shop window: one read that answers what exists, what it costs and what is already on.' operationId: get_marketplace responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.marketCatalog' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists every tool and agent the caller can reach in their own org and project, enriched with any public listing's title, category and price, and with installed=true on the ones already activated for that scope. tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/install: post: description: "Activates one tool for the caller's own org and project. A marketplace\ninstall IS the tool plane's activation\ \ write \u2014 one store, one truth \u2014 so an\ninstalled capability is immediately dispatchable and a monetized\ \ one is priced\nfrom its listing at every call. The tool must resolve in the caller's scope, so\ninstalling something\ \ that does not exist is refused rather than recorded." operationId: post_marketplace_install requestBody: content: application/json: example: tool: summarize schema: $ref: '#/components/schemas/marketplace.installReq' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.installState' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Activates one tool for the caller's own org and project. tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/jobs: get: description: "Lists the jobs the caller's org is a party to, newest first: as buyer, the work\nit hired \u2014 with\ \ the quotes it has not paid and the jobs being funded, each with\nthe attempt that made it, so a hire whose answer\ \ was lost is found here \u2014 and as\nseller, the work it was hired for, which never includes another org's quote.\n\ Any member of the org." operationId: get_marketplace_jobs parameters: - description: "Role is buyer \u2014 the jobs the caller's org hired for \u2014 or seller, the jobs it\nwas hired for.\ \ Buyer when empty." example: seller in: query name: role required: false schema: type: string - description: Status keeps one state. example: open in: query name: status required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.jobPage' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the jobs the caller's org is a party to, newest first: as buyer, the work it hired \u2014 with the quotes\ \ it has not paid and the jobs being funded, each with the attempt that made it, so a hire whose answer was lost is\ \ found here \u2014 and as seller, the work it was hired for, which never includes another org's quote." tags: - marketplace x-app: marketplace x-public: true x-tool: true post: description: "Hires another org for a piece of work, through a public listing or by a direct\noffer, and answers 201\ \ with the job \u2014 open, waiting for the seller.\n\nIt is paid over x402, in two steps on this one call. Sent without\ \ payment, the\nterms are cleared first \u2014 principal decides whether the buyer may pay the seller\nthis amount,\ \ and a blocked payment, one that waits on the seller's tax form, and\none that clears only with tax withheld, which\ \ this rail does not withhold, are\nrefused and nothing is opened \u2014 and it answers 402 with the terms to sign,\ \ for\nthe job resource job:: on PAYMENT-REQUIRED, in the body, and in the message.\nSent again with the signed\ \ authorization \u2014 on PAYMENT-SIGNATURE, or as\npayment \u2014 echoing that resource, the terms are cleared once\ \ more, the job is\nfunding, and the rail checks the payment against exactly those terms, checks\nit stays acceptable\ \ until a dispute could last be ruled on, holds it for this\njob, and sets the amount aside in the buyer's wallet:\ \ a wallet that cannot\ncover it answers 402 insufficient_funds and opens nothing. Nothing is paid\nuntil the buyer\ \ releases the job, or its review window passes after delivery;\na job that ends unpaid returns the amount to the\ \ wallet. Any member of the\nbuying org; a platform SuperAdmin inspecting another org cannot spend it.\n\nA hire sent\ \ with an attempt \u2014 the buyer's own key for it, in the body \u2014 is one\nhire however often it is sent: the\ \ same attempt with the same request answers\nthe same job, so a request whose answer was lost is sent again as it\ \ was, and\nopens one job and sets its amount aside once. It answers the quote's terms\nagain while the job is quoted\ \ or funding (a quote over its hour is given up\nand quoted anew), and the job, 201, once it opened. Another request\ \ under an\nattempt already used is refused, 422. The buyer's own jobs list its quotes\nand those being funded, with\ \ their attempts, so an attempt in flight is found\nthere too. Without an attempt, the same terms asked again within\ \ the hour\nanswer the same quote, and the same payment sent again answers the job it\nopened." operationId: post_marketplace_jobs requestBody: content: application/json: example: amount: '250.00' attempt: 8f14e45f-ea8d-4c4e-9c1b-0f3b2b1a7d6e brief: Summarize our Q3 filings listing: lst_4f1c schema: $ref: '#/components/schemas/marketplace.hireIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/marketplace.Job' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Hires another org for a piece of work, through a public listing or by a direct offer, and answers 201 with\ \ the job \u2014 open, waiting for the seller." tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/jobs/{id}: get: description: 'Reads one job the caller''s org is a party to. Another org''s job, and one that does not exist, are the same 404.' operationId: get_marketplace_jobs_by_id parameters: - description: ID is the job, from the path. example: job_9a1f in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.Job' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads one job the caller's org is a party to. tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/jobs/{id}/accept: post: description: 'Accepts a job the caller''s org was hired for: the seller takes the work on and the clock toward its deadline is the seller''s. Only an open job, and only before its deadline.' operationId: post_marketplace_jobs_by_id_accept parameters: - description: ID is the job, from the path. example: job_9a1f in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.Job' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Accepts a job the caller''s org was hired for: the seller takes the work on and the clock toward its deadline is the seller''s.' tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/jobs/{id}/cancel: post: description: 'Takes back a job the caller''s org opened, before the seller accepts it. Nothing was paid: the amount set aside returns to the wallet.' operationId: post_marketplace_jobs_by_id_cancel parameters: - description: ID is the job, from the path. example: job_9a1f in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.Job' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Takes back a job the caller's org opened, before the seller accepts it. tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/jobs/{id}/decline: post: description: 'Declines a job the caller''s org was hired for, before any work. Nothing was paid: the buyer''s authorization is given up, never settled, and the amount it set aside returns to the buyer''s wallet.' operationId: post_marketplace_jobs_by_id_decline parameters: - description: ID is the job, from the path. example: job_9a1f in: path name: id required: true schema: type: string requestBody: content: application/json: example: id: job_9a1f reason: Booked through March schema: $ref: '#/components/schemas/marketplace.declineIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.Job' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Declines a job the caller's org was hired for, before any work. tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/jobs/{id}/deliver: post: description: 'Records delivery of a job the caller''s org accepted, before its deadline, and starts the review window: the buyer releases or disputes within it, or it releases itself when it closes.' operationId: post_marketplace_jobs_by_id_deliver parameters: - description: ID is the job, from the path. example: job_9a1f in: path name: id required: true schema: type: string requestBody: content: application/json: example: id: job_9a1f note: Summary attached url: https://files.example.com/q3.pdf schema: $ref: '#/components/schemas/marketplace.deliverIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.Job' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Records delivery of a job the caller''s org accepted, before its deadline, and starts the review window: the buyer releases or disputes within it, or it releases itself when it closes.' tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/jobs/{id}/dispute: post: description: 'Stops a job for a ruling, as either party: before delivery (and before the deadline), or within the review window after it. Nothing moves while it is disputed. It ends when the buyer releases it, the seller refunds it, the platform''s arbiter rules, or the arbiter''s time lapses and it is refunded.' operationId: post_marketplace_jobs_by_id_dispute parameters: - description: ID is the job, from the path. example: job_9a1f in: path name: id required: true schema: type: string requestBody: content: application/json: example: id: job_9a1f reason: The summary covers Q2, not Q3 schema: $ref: '#/components/schemas/marketplace.disputeIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.Job' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Stops a job for a ruling, as either party: before delivery (and before the deadline), or within the review window after it.' tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/jobs/{id}/feedback: post: description: "Rates the other party of a settled job \u2014 the seller when the caller's org\nbought, the buyer when\ \ it sold \u2014 once per party per job, and never edited. A\njob counts as settled once it was released, or refunded\ \ after the seller took\nit on; one cancelled or declined before any work earns nothing. What buyers say\nof a seller\ \ is its listing's and its own reputation in the shop. It answers 201\nwith the feedback recorded." operationId: post_marketplace_jobs_by_id_feedback parameters: - description: ID is the job, from the path. example: job_9a1f in: path name: id required: true schema: type: string requestBody: content: application/json: example: comment: Thorough and on time id: job_9a1f rating: 5 schema: $ref: '#/components/schemas/marketplace.feedbackIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/marketplace.Feedback' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Rates the other party of a settled job \u2014 the seller when the caller's org bought, the buyer when it sold\ \ \u2014 once per party per job, and never edited." tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/jobs/{id}/refund: post: description: 'Refunds a job the caller''s org accepted, as the seller, at any point before it is paid: nothing was moved, so the buyer''s authorization is given up, never settled, and the amount it set aside returns to the buyer''s wallet.' operationId: post_marketplace_jobs_by_id_refund parameters: - description: ID is the job, from the path. example: job_9a1f in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.Job' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Refunds a job the caller''s org accepted, as the seller, at any point before it is paid: nothing was moved, so the buyer''s authorization is given up, never settled, and the amount it set aside returns to the buyer''s wallet.' tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/jobs/{id}/release: post: description: 'Releases a job the caller''s org is paying for, paying the seller the whole amount: the buyer''s authorization is settled on the rail, once, and the rail states the payment. The buyer may release any time after acceptance, including to end a dispute.' operationId: post_marketplace_jobs_by_id_release parameters: - description: ID is the job, from the path. example: job_9a1f in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.Job' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Releases a job the caller''s org is paying for, paying the seller the whole amount: the buyer''s authorization is settled on the rail, once, and the rail states the payment.' tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/jobs/{id}/resolve: post: description: 'Rules on a disputed job, as the platform''s arbiter: release pays the seller the whole amount from the buyer''s held authorization, if the payment still clears; refund ends it with nothing paid, and the amount set aside returns to the buyer''s wallet. A platform SuperAdmin only.' operationId: post_marketplace_jobs_by_id_resolve parameters: - description: ID is the job, from the path. example: job_9a1f in: path name: id required: true schema: type: string requestBody: content: application/json: example: id: job_9a1f outcome: refund schema: $ref: '#/components/schemas/marketplace.resolveIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.Job' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Rules on a disputed job, as the platform''s arbiter: release pays the seller the whole amount from the buyer''s held authorization, if the payment still clears; refund ends it with nothing paid, and the amount set aside returns to the buyer''s wallet.' tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/listings: get: description: "Returns the listings the caller's own org has published \u2014 what this\norg is offering, not what it\ \ can buy. A publisher only ever sees its own rows." operationId: get_marketplace_listings responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.listingPage' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the listings the caller's own org has published \u2014 what this org is offering, not what it can\ \ buy." tags: - marketplace x-app: marketplace x-public: true x-tool: true post: description: "Offers one thing the caller's org owns on the marketplace \u2014 an agent,\npersona, app, skill or MCP\ \ server, or, for the platform, a tool sold per call \u2014\noptionally monetized. The owning app is asked, as the\ \ org, whether the thing is\nthe org's, so a listing never sells something that does not exist or belongs to\nsomeone\ \ else: a tool every org reaches is the platform's, and a server enabled\noff the public shelf is the shelf's. A monetized\ \ listing names a payout wallet of\nthe org's own, which wallets confirms, so a price is never unpayable. The price\n\ is exact to 18 decimal places, so a per-call price below a cent is a real price\nand not a rounded-away zero. An org\ \ admin publishes, at most 1000 listings an\norg; it answers 201 with the created row." operationId: post_marketplace_listings requestBody: content: application/json: example: kind: agent price: '250' public: true recipient: wal_9f2 title: Research analyst tool: researcher schema: $ref: '#/components/schemas/marketplace.publishReq' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/marketplace.Listing' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Offers one thing the caller's org owns on the marketplace \u2014 an agent, persona, app, skill or MCP server,\ \ or, for the platform, a tool sold per call \u2014 optionally monetized." tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/listings/{id}: delete: description: 'Withdraws one of the caller org''s listings from the marketplace and answers 204. Only the publishing org can remove its own listing; an id that is unknown, or belongs to another org, is the same 404, so a probe learns nothing about what exists. Removing a listing removes its price from per-call enforcement; it does not uninstall the tool for anyone who already installed it, and a job already opened through it runs to its end. An org admin unpublishes.' operationId: delete_marketplace_listings_by_id parameters: - description: ID is the listing, from the path. example: lst_1 in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Withdraws one of the caller org's listings from the marketplace and answers 204. tags: - marketplace x-app: marketplace x-public: true x-tool: true patch: description: "Edits one of the caller org's listings \u2014 its copy, price, payout wallet,\nvisibility and documentation\ \ \u2014 under the same rules publish applies, and\nanswers the listing as it now stands. Another org's listing and\ \ one that does\nnot exist are the same 404. An org admin edits." operationId: patch_marketplace_listings_by_id parameters: - description: ID is the listing to edit, from the path. example: lst_4f1c in: path name: id required: true schema: type: string requestBody: content: application/json: example: id: lst_4f1c price: '300' public: true schema: $ref: '#/components/schemas/marketplace.patchReq' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.Listing' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Edits one of the caller org's listings \u2014 its copy, price, payout wallet, visibility and documentation\ \ \u2014 under the same rules publish applies, and answers the listing as it now stands." tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/seller: get: description: "Answers where the caller's org stands as a seller, in one read: its founders'\nidentity verification (KYC)\ \ and legal entity (KYB), its tax form and whether it\nis certified and valid, its own sanctions screening \u2014\ \ only what an org may see\nabout itself \u2014 the payout wallet it proved, the TaxPrincipalCredentials signed\n\ for its agents, what it earned in the year from the economic events the rails\nstated, and the 1099s payers furnished\ \ it. Ready says nothing is missing. Each\nowning app is asked as the org; one this deployment does not run is named\ \ absent\nrather than read as empty. An org admin reads it." operationId: get_marketplace_seller parameters: - description: Year is the calendar year (UTC); the current one when zero. example: 2026 in: query name: year required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.Onboarding' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Answers where the caller's org stands as a seller, in one read: its founders' identity verification (KYC)\ \ and legal entity (KYB), its tax form and whether it is certified and valid, its own sanctions screening \u2014 only\ \ what an org may see about itself \u2014 the payout wallet it proved, the TaxPrincipalCredentials signed for its\ \ agents, what it earned in the year from the economic events the rails stated, and the 1099s payers furnished it." tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/seller/payout: post: description: 'Starts binding one of the caller org''s wallets as the wallet it is paid into: answers a challenge naming the org, the wallet and its address, to be signed with that wallet within fifteen minutes and sent to POST /v1/marketplace/seller/payout/verify. A newer challenge replaces an older one. An org admin binds.' operationId: post_marketplace_seller_payout requestBody: content: application/json: example: wallet: wal_9f2 schema: $ref: '#/components/schemas/marketplace.payoutIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.PayoutChallenge' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Starts binding one of the caller org''s wallets as the wallet it is paid into: answers a challenge naming the org, the wallet and its address, to be signed with that wallet within fifteen minutes and sent to POST /v1/marketplace/seller/payout/verify.' tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/seller/payout/verify: post: description: 'Binds the caller org''s payout wallet: the signature over its outstanding challenge is recovered, and must recover to the wallet''s address, before the challenge expires. A challenge binds once. It answers the payout wallet as it now stands; a job offered directly to the org is paid into it. An org admin binds.' operationId: post_marketplace_seller_payout_verify requestBody: content: application/json: example: signature: "0x5f1c\u20261b" schema: $ref: '#/components/schemas/marketplace.verifyIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.Payout' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Binds the caller org''s payout wallet: the signature over its outstanding challenge is recovered, and must recover to the wallet''s address, before the challenge expires.' tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/shop: get: description: "Searches every public listing of every kind \u2014 agents and personas to hire,\napps, skills, MCP servers\ \ and tools \u2014 newest first, with facets by kind,\ncategory, price and rating, paged. Each listing carries its\ \ seller's public\nface, its reputation from settled jobs and installs, and the command and MCP\noperation that buy\ \ it. It needs no credential and answers the same to everyone." operationId: get_marketplace_shop parameters: - description: 'Q keeps listings whose title, description, category, thing or seller contains every word of it, case-insensitively.' example: research in: query name: q required: false schema: type: string - description: 'Kind keeps one kind: agent, persona, app, skill, mcp or tool.' example: agent in: query name: kind required: false schema: type: string - description: Category keeps one category, exactly. in: query name: category required: false schema: type: string - description: Price keeps free listings or priced ones. in: query name: price required: false schema: type: string - description: Rating keeps listings rated at least this many stars, 1 to 5. in: query name: rating required: false schema: format: int64 type: integer - description: Seller keeps one seller org's listings. in: query name: seller required: false schema: type: string - description: 'Limit is the page size: 48 by default, 200 at most.' example: 48 in: query name: limit required: false schema: format: int64 type: integer - description: Offset is where the page starts. in: query name: offset required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.Shop' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused security: [] summary: "Searches every public listing of every kind \u2014 agents and personas to hire, apps, skills, MCP servers\ \ and tools \u2014 newest first, with facets by kind, category, price and rating, paged." tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/shop/{id}: get: description: "Reads one public listing as the shop shows it \u2014 its seller's public face, its\nreputation and the\ \ ways to buy it. A private listing and one that does not\nexist are the same 404. It needs no credential." operationId: get_marketplace_shop_by_id parameters: - description: ID is the listing, from the path. example: lst_4f1c in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.ShopListing' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused security: [] summary: "Reads one public listing as the shop shows it \u2014 its seller's public face, its reputation and the ways\ \ to buy it." tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/marketplace/uninstall: post: description: "Deactivates one tool for the caller's own org and project, so it stops\nbeing dispatchable there. It is\ \ the exact inverse of install and touches the same\nactivation record; deactivating something that was never active\ \ is not an error.\nThe listing itself is untouched \u2014 this withdraws the caller's use of a capability,\nnot anyone's\ \ offer of it." operationId: post_marketplace_uninstall requestBody: content: application/json: example: tool: summarize schema: $ref: '#/components/schemas/marketplace.installReq' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/marketplace.installState' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deactivates one tool for the caller's own org and project, so it stops being dispatchable there. tags: - marketplace x-app: marketplace x-public: true x-tool: true /v1/mcp: post: description: "Model Context Protocol over JSON-RPC 2.0 \u2014 one POST per message, stateless, protocol revision 2026-07-28.\ \ tools/list answers without a credential with one tool per subsystem (its operations in the \"op\" enum) plus \"\ describe\", which returns one operation's input schema. tools/call names a subsystem tool and carries {\"op\": ,\ \ \"input\": }; it takes the same bearer the REST API does, and a call that carries none is answered\ \ 401 with a WWW-Authenticate header naming the resource metadata at /.well-known/oauth-protected-resource, which\ \ names the authorization server to sign in at. The tool surface is the public contract: the operator's admin product\ \ is not offered, and a name that would disclose a secret or mutate an identity is withheld \u2014 the list says how\ \ many, under _meta." operationId: post_mcp requestBody: content: application/json: schema: $ref: '#/components/schemas/MCPRequest' responses: 2XX: content: application/json: schema: $ref: '#/components/schemas/MCPResponse' description: Success summary: 'The agent endpoint: every subsystem''s operations as MCP tools' tags: - openapi x-app: openapi x-public: true /v1/meet/call: get: description: "Answers where a room's call happens, for a caller who may join it.\n\nIt is the \"resolved at render\"\ \ half of HIP-0523 \xA712: a surface showing a channel\nasks for the room's call at the moment it draws one, rather\ \ than reading a media\nroom name someone stored on the room. Nothing here is persisted and nothing is\ncreated \u2014\ \ a media room begins existing when the first participant connects and\nstops when the last leaves, so there is no\ \ call to create and none to clean up.\n\nAUTHORIZATION IS THE JOIN DECISION, unchanged and shared. It delegates to\n\ state.admits, the same function POST /v1/meet/getToken and all three recording\noperations admit on, so a caller who\ \ is told where a call is, is a caller who\ncould have joined it. Answering the address to someone who cannot join\ \ would make\nthis a space-membership oracle for anyone who can guess a room id.\n\nIt deliberately does NOT report\ \ whether a call is in progress. That is a fact the\nmedia server holds and this binary would have to ask for it over\ \ the network,\nwhich is a different decision with a different failure mode \u2014 and reporting\n\"nobody is in this\ \ call\" when the question could not be asked would be exactly the\nunknown-rendered-as-zero this surface refuses\ \ elsewhere." operationId: meetCall parameters: - description: 'Space is the space uuid holding the room, as GET /v1/team/rooms reports it. It is the segment the caller''s membership is checked against.' in: query name: space required: true schema: type: string - description: 'Room is the room''s own id within that space, as GET /v1/team/rooms reports it. It is opaque here: meet keeps no rooms and cannot say whether one exists, only whether this caller may be seated in the space holding it.' in: query name: room required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/meet.venue' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Where a room's call happens tags: - meet x-app: meet x-public: true x-tool: true /v1/meet/getToken: post: description: "Answers with a LiveKit join token for exactly the room named in the body. The body is the RAW token as\ \ text/plain \u2014 one opaque string, not JSON and not wrapped in an envelope, which is what the office client reads.\n\ \nThe caller presents its space session as a Bearer. Every clause is a refusal: the session must verify, its SIGNED\ \ space claim must equal the room's leading name segment \u2014 rooms are named `__`, and that prefix\ \ is the only thing binding a room to a tenant \u2014 and the session must carry a privileged space role, so a guest\ \ is refused rather than seated.\n\nThe participant identity is the SESSION'S, never the body's. `_id` is accepted\ \ for compatibility with the published client bundle and deliberately ignored: LiveKit treats the identity as unique\ \ and ejects a duplicate, so honouring a caller-chosen one would let anyone in a space kick out a colleague and impersonate\ \ them. `participantName` is a display name only.\n\nAn unconfigured deployment answers 503 under its own name rather\ \ than 404, and the refusal states only that the office is unconfigured \u2014 the reason names key material and stays\ \ in the boot log." operationId: post_meet_gettoken summary: Mint a join token for one video room tags: - meet x-app: meet x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/meet/health: get: description: "Reports whether the office can mint join tokens.\n\nIt reports whether this deployment holds the LiveKit\ \ key pair it needs:\nready:true with 200 when tokens can be minted, the SAME body with ready:false,\nstatus \"degraded\"\ \ and 503 when they cannot \u2014 so a probe and a dashboard both\nread the degraded state instead of someone grepping\ \ a boot log.\n\nIt takes no credential and is reachable on every public host, so it withholds\nboth the reason and\ \ the signing key's name on purpose: ready is the whole\ndashboard fact, and the reason \u2014 which names the key\ \ file and the Secret \u2014 is\nwritten to the boot log where an operator already is." operationId: get_meet_health responses: '200': content: application/json: schema: $ref: '#/components/schemas/meet.meetHealth' description: ok '503': content: application/json: schema: $ref: '#/components/schemas/meet.meetHealth' description: service unavailable default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports whether the office can mint join tokens. tags: - meet x-app: meet x-public: true x-tool: true /v1/meet/record: delete: description: "Ends a room's recording \u2014 EVERY one of them.\n\nWhoever the room admits may stop it, including someone\ \ who did not start it:\na person being recorded has to be able to end it, and a rule that only the\nstarter may stop\ \ would deny exactly that. Stopping is free \u2014 a caller made to\npay to stop being recorded would be paying for\ \ the wrong thing.\n\n200 MEANS THE ROOM IS NOT BEING RECORDED, and that is why this ends all of them\nrather than\ \ the first. \"At most one per room\" is an invariant this surface wants\nand cannot impose: reading the list and\ \ starting are two calls, and two replicas\nracing through that window both start. When the list comes back holding\ \ two, two\nis the truth \u2014 and ending one while answering 200 tells the person withdrawing\nconsent that it stopped\ \ while a second worker keeps writing. A stop that cannot\nfinish the job says so instead.\n\nStopping a room that\ \ is not being recorded is not an error. The answer names the\nroom with no recording on it, which is the state the\ \ caller asked for." operationId: meetRecordStop parameters: - description: 'Room is the LiveKit room, named the way the office client names one (`__`). Its leading segment is what binds the room to a tenant, and it is the segment the caller''s membership is checked against.' in: query name: room required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/meet.recording' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Stop a room's recording tags: - meet x-app: meet x-public: true x-tool: true get: description: "Answers what is being recorded in a room, and where the file went.\n\nIt reports the recording that is\ \ RUNNING, and once none is, the most recent one\nthe media server still holds \u2014 with its final status and its\ \ object. That second\ncase is the one that matters for finding a file: the answer to a start is the\nonly other place\ \ the location appears, and a client that lost it, or a colleague\nwho was not the one to press record, has nowhere\ \ else to look.\n\nIt is behind the same check as starting one: where a recording of a private\nconversation is kept\ \ is a fact about that conversation, so it is told to the\npeople the room admits and to nobody else." operationId: meetRecordRead parameters: - description: 'Room is the LiveKit room, named the way the office client names one (`__`). Its leading segment is what binds the room to a tenant, and it is the segment the caller''s membership is checked against.' in: query name: room required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/meet.recording' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: What is being recorded in a room, and where the file goes tags: - meet x-app: meet x-public: true x-tool: true post: description: "Begins recording a room, or hands back the recording already running.\n\nA recording is a durable artifact\ \ of a conversation, so only someone this room\nwould admit may make one: the caller is authorized by the SAME decision\n\ /v1/meet/getToken makes about the same room, and refused with the same 401.\n\nA SECOND START RETURNS THE FIRST rather\ \ than refusing it. There is at most one\nrecording per room and this operation's job is to establish that there is\ \ one \u2014\nwhich is already true when a colleague, or the caller's own double-click,\nstarted it a moment ago.\ \ The answer is the same shape either way, naming the\nrecording that is actually running, so a client never has to\ \ tell the two cases\napart to find the id.\n\nA deployment with no media server address or no object store answers\ \ 503 naming\nwhich, because a recording that silently does not happen is worse than one that\nis refused. The reason\ \ reaches only a caller this room already admits." operationId: meetRecordStart requestBody: content: application/json: schema: $ref: '#/components/schemas/meet.recordIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/meet.recording' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Start recording a room, or return the recording already running tags: - meet x-app: meet x-public: true x-tool: true /v1/meet/session: get: description: "Answers the three facts the native lobby cannot know on its own: the identity a seat would be taken under,\ \ the LiveKit address the browser dials, and the spaces this caller may open a room in.\n\nIt is the SAME decision\ \ getToken makes, asked before the room exists rather than after it is named. A room is bound to its tenant by its\ \ name's leading space segment, and only a space this answer lists will be admitted \u2014 so the lobby offers exactly\ \ what the mint would grant, and a person is never shown a room they would then be refused. Spaces the caller holds\ \ only a guest role in are omitted for that reason.\n\nAn empty list is a real answer, not a fault: an IAM identity\ \ with no space has no room to open, and the lobby says so instead of failing.\n\n`ws` is empty when this deployment\ \ has not been told where its media plane is (LIVEKIT_WS). Token minting is unaffected \u2014 the published office\ \ client supplies its own address \u2014 so this is a degraded native UI, not a degraded service, and the lobby refuses\ \ to dial rather than guessing a host." operationId: get_meet_session summary: What this caller may open a room in tags: - meet x-app: meet x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/messages: post: description: Implements the Anthropic Messages API. operationId: post_messages requestBody: content: application/json: schema: $ref: '#/components/schemas/ai.AnthropicRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/ai.AnthropicResponse' description: Success. '401': description: No credential, or one this service does not accept. '403': description: A valid credential that may not do this. summary: Implements the Anthropic Messages API. tags: - ai x-app: ai x-public: true /v1/messages/count_tokens: post: description: 'Implements POST /v1/messages/count_tokens. Claude Code calls it before a request; it returns {"input_tokens": N} for the given model + messages + tools.' operationId: post_messages_count_tokens responses: '200': content: application/json: schema: $ref: '#/components/schemas/ai.TokenCount' description: Success. '401': description: No credential, or one this service does not accept. '403': description: A valid credential that may not do this. summary: Implements POST /v1/messages/count_tokens. tags: - ai x-app: ai x-public: true /v1/metrics/batch: post: description: "Writes every sample in a luxfi/metric `MetricBatch` into the calling org's store and answers `{written}`:\ \ the number of SAMPLES stored, not families and not metrics. This is the exact wire shape the ZAP `MsgMetricBatch`\ \ transport carries, so the HTTP endpoint and the optional ZAP push receiver share one code path and one meaning \u2014\ \ the transport is an optimisation, never a different contract.\n\nA counter or gauge lands as one sample. A histogram\ \ or summary contributes DERIVED `_sum` and `_count` series, so one metric can write more than one sample\ \ and `written` can exceed the number of metrics you sent. The batch's own `TimestampNs` stamps every sample it carries.\n\ \nThe tenant is the gateway-minted `X-Org-Id` header, falling back to the deployment brand and then `default`; each\ \ org gets its own store, WAL-durable under the deployment's data dir. A body that does not decode is 400." operationId: post_metrics_batch summary: "Ingest a MetricBatch \u2014 the same payload the ZAP transport carries" tags: - metrics x-app: metrics x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/metrics/health: get: description: "Reports the native metrics store's live state for the calling tenant: the subsystem version, the resolved\ \ `org`, and `series` \u2014 the number of distinct series actually held right now, read out of the store rather than\ \ a constant. It is not a dependency probe and has nothing downstream to fail on: the store is in-process, so this\ \ answers 200 whenever the process is up.\n\nThe tenant is the gateway-minted `X-Org-Id` header, falling back to the\ \ deployment brand and then `default`. This surface trusts the edge rather than re-deriving the org from a validated\ \ claim of its own, so it belongs behind the gateway and nowhere else." operationId: get_metrics_health summary: How many metric series this deployment holds for your org tags: - metrics x-app: metrics x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/metrics/logs/health: get: description: "Reports the native log store's live state for the calling tenant: the subsystem version and `records`,\ \ the count actually held right now rather than a constant. Not a dependency probe \u2014 the store is in-process,\ \ so this answers 200 whenever the process is up.\n\nThe tenant is the gateway-minted `X-Org-Id` header, falling back\ \ to the deployment brand and then `default`." operationId: get_metrics_logs_health summary: How many log records this deployment holds for your org tags: - metrics x-app: metrics x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/metrics/logs/query: get: description: "Answers `{count, records}`, newest first. `match` is the same `k=v,k2=v2` superset label matcher the metrics\ \ query uses; `contains` is a case-insensitive substring test against the record body; `start` and `end` are nanosecond\ \ bounds.\n\nA bound that is absent, empty or unparseable becomes 0, which means UNBOUNDED \u2014 a malformed `start`\ \ widens the search rather than failing it. `limit` caps the page and defaults to 100 when absent or non-positive,\ \ so an unfiltered read is never the whole ring.\n\nThe tenant is the gateway-minted `X-Org-Id` header, falling back\ \ to the deployment brand and then `default`, so a search can only reach the org the edge asserted." operationId: get_metrics_logs_query summary: Search your org's logs by label, time and substring tags: - metrics x-app: metrics x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/metrics/logs/write: post: description: "Takes `{records:[{t, level, body, labels}]}`, appends each one, and answers `{written}`. Bodies are stored\ \ verbatim; `labels` are the indexed dimensions a query filters on, so what you do not label you can only find by\ \ substring.\n\n`t` is NANOSECONDS since the Unix epoch. A record sent without one is stored at 0 and then falls outside\ \ any query carrying a lower bound \u2014 the usual reason a successful write does not read back. Retention is a bounded\ \ ring, 1048576 records per org, oldest evicted first. No record is validated or rejected, so `written` is the number\ \ of records SENT; only a body that does not decode at all is 400.\n\nThe tenant is the gateway-minted `X-Org-Id`\ \ header, falling back to the deployment brand and then `default`; each org's records live in its own WAL-durable\ \ store." operationId: post_metrics_logs_write summary: Append structured log records for your org tags: - metrics x-app: metrics x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/metrics/query: get: description: "Answers `{count, series}`, where `count` is the number of matching SERIES and each series carries the\ \ samples that fall inside the window. `name` selects one series name, and an absent or empty `name` returns every\ \ series the org holds. `match` is a `k=v,k2=v2` label matcher applied as a SUPERSET test: a series matches when it\ \ carries all the named labels with those values, extra labels and all.\n\n`start` and `end` are nanoseconds since\ \ the Unix epoch, and here is the rule worth knowing: a bound that is absent, empty or unparseable becomes 0, which\ \ this store reads as UNBOUNDED. A malformed `start` therefore silently widens the query instead of failing it. There\ \ is no limit parameter \u2014 the window and the matcher are the whole of what bounds the answer.\n\nThe tenant is\ \ the gateway-minted `X-Org-Id` header, falling back to the deployment brand and then `default`, so a query can only\ \ ever read the org the edge asserted." operationId: get_metrics_query summary: Read your org's series back over a time range tags: - metrics x-app: metrics x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/metrics/traces/health: get: description: "Reports the native trace store's live state for the calling tenant: the subsystem version and `spans`,\ \ the count actually held right now. Not a dependency probe \u2014 the store is in-process, so this answers 200 whenever\ \ the process is up.\n\nThe tenant is the gateway-minted `X-Org-Id` header, falling back to the deployment brand and\ \ then `default`." operationId: get_metrics_traces_health summary: How many spans this deployment holds for your org tags: - metrics x-app: metrics x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/metrics/traces/query: get: description: "Answers `{count, spans}`, newest first, filtered on each span's START time. `start` and `end` are nanosecond\ \ bounds where 0 \u2014 which is what an absent, empty or unparseable value becomes \u2014 means UNBOUNDED, so a malformed\ \ bound widens the listing instead of failing it. `limit` defaults to 100 when absent or non-positive.\n\nIt lists\ \ SPANS, not traces: several spans of one trace each count separately and each take a slot against `limit`. Assembling\ \ one trace is /v1/metrics/traces/trace. The tenant is the gateway-minted `X-Org-Id` header, falling back to the deployment\ \ brand and then `default`." operationId: get_metrics_traces_query summary: Recent spans for your org over a time range tags: - metrics x-app: metrics x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/metrics/traces/trace: get: description: "Answers `{spans}`: every span the org holds for the trace id in `id`, in the order they were appended,\ \ which is what a waterfall view renders. Unlike the other reads there is no count, no time range and no limit \u2014\ \ a trace is addressed by id or not at all.\n\nAn id with no spans answers an EMPTY list, never a 404: the store cannot\ \ tell a trace that never existed from one whose spans retention has already dropped, so it does not pretend to. The\ \ tenant is the gateway-minted `X-Org-Id` header, falling back to the deployment brand and then `default`, and a trace\ \ id belonging to another org is simply not in this org's store." operationId: get_metrics_traces_trace summary: "Every span of one trace \u2014 the waterfall" tags: - metrics x-app: metrics x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/metrics/traces/write: post: description: "Takes `{spans:[{traceId, spanId, parentId, name, startNs, endNs, attrs}]}`, appends each, and answers\ \ `{written}` \u2014 the number of spans sent. Every span is indexed by its trace id as it lands, which is what makes\ \ the waterfall read possible without a second store.\n\nTimes are NANOSECONDS since the Unix epoch. Retention is\ \ a bounded ring of 1048576 spans per org: past that the OLDEST are evicted to keep the newest 1048576, and the trace\ \ index is rebuilt \u2014 so a long-lived trace can lose its early spans while its later ones survive, and a waterfall\ \ read is best-effort against retention, not a guarantee.\n\nThe tenant is the gateway-minted `X-Org-Id` header, falling\ \ back to the deployment brand and then `default`. A body that does not decode is 400." operationId: post_metrics_traces_write summary: Append spans for your org tags: - metrics x-app: metrics x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/metrics/write: post: description: "Takes `{series:[{name, labels, samples:[{t, v}]}]}`, appends every sample, creating each series on first\ \ write, and answers `{written}` \u2014 again counting SAMPLES, so three series of ten samples is 30.\n\nA series\ \ is identified by its name PLUS its whole label set, so adding one label makes a different series rather than annotating\ \ an existing one. Timestamps `t` are NANOSECONDS since the Unix epoch; a sample sent without one is stored at 0 and\ \ is then excluded by any query that sets a lower bound, which is the usual reason a write that reported success does\ \ not read back. Retention is per series and bounded \u2014 past 65536 samples the oldest are evicted.\n\nThe tenant\ \ is the gateway-minted `X-Org-Id` header, falling back to the deployment brand and then `default`. A body that does\ \ not decode is 400; nothing else is validated or rejected." operationId: post_metrics_write summary: Append samples to your org's named, labelled series tags: - metrics x-app: metrics x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/ml/health: get: description: "Reports whether the model-serving plane is genuinely usable: that the Kubernetes API answers, that the\ \ InferenceService CRD is actually served by this cluster, and that the cluster holds at least one serving runtime\ \ to run a model ON. It is a REAL probe, not status theatre \u2014 it makes a live call rather than reporting a flag\ \ set at boot.\n\n200 only when everything checks out. Otherwise 503 CARRYING THE REPORT \u2014 which component failed,\ \ and the real error \u2014 and that body is the reason this is not a typed op: a typed op reaches a non-2xx by returning\ \ an error, and the envelope that produces would drop exactly the detail the probe exists to deliver.\n\nThe runtime\ \ count is reported as its own field and is a SEPARATE fact from the CRD being served: a cluster with the CRD but\ \ no runtime accepts a deploy and then never schedules it, so reporting only the CRD would answer 200 while every\ \ model hangs. A runtime list this service cannot read reports the read error instead of a count, because a missing\ \ grant is a broken probe and not an empty cluster.\n\nIt answers about the cluster, not about a tenant, so it takes\ \ no org and reveals no tenant data. A cluster with no kserve CRD reports degraded honestly rather than failing later\ \ at the first deploy." operationId: get_ml_health summary: Whether model serving can actually work right now tags: - ml x-app: ml x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/ml/models: get: description: "Lists the inference models deployed in the caller's org. Each entry\ncarries the model's name, when Kubernetes\ \ admitted it, and kserve's live status\n\u2014 the spec is on the single-model read. An org that has deployed nothing\ \ gets\nan empty list." operationId: get_ml_models responses: '200': content: application/json: schema: $ref: '#/components/schemas/ml.mlResourceList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the inference models deployed in the caller's org. tags: - ml x-app: ml x-public: true x-tool: true post: description: "Deploys one inference model for the caller's org, and answers 201\nwith the model as Kubernetes admitted\ \ it.\n\nThe `spec` is a kserve InferenceService spec, passed through unchanged \u2014 this\nplane owns the tenancy,\ \ the billing and the namespace, and kserve owns what a\nmodel IS. An unfunded org is refused BEFORE anything is created,\ \ so nobody runs\nfree GPU compute and nobody is charged for a resource that was never made." operationId: post_ml_models requestBody: content: application/json: example: name: sentiment spec: predictor: model: modelFormat: name: sklearn storageUri: s3://models/sentiment schema: $ref: '#/components/schemas/ml.mlCreate' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/ml.mlResource' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deploys one inference model for the caller's org, and answers 201 with the model as Kubernetes admitted it. tags: - ml x-app: ml x-public: true x-tool: true /v1/ml/models/{name}: delete: description: 'Deletes a deployed inference model. kserve owns the teardown: the InferenceService goes away and the serving deployment behind it follows, so the model stops answering predict calls. Answers 204, or 404 for a name the caller''s org does not own.' operationId: delete_ml_models_by_name parameters: - description: 'Name is the resource to act on, taken from the path. Lower-cased and trimmed to the DNS-1123 label a CustomResource''s metadata.name must be.' example: sentiment in: path name: name required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes a deployed inference model. tags: - ml x-app: ml x-public: true x-tool: true get: description: 'Returns one deployed inference model. Its spec comes with it, and kserve''s live status, which is where readiness and the serving address appear. A name the caller''s org does not own answers 404, exactly as an unknown name does, so a probe learns nothing about another tenant''s models.' operationId: get_ml_models_by_name parameters: - description: 'Name is the resource to act on, taken from the path. Lower-cased and trimmed to the DNS-1123 label a CustomResource''s metadata.name must be.' example: sentiment in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/ml.mlResource' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one deployed inference model. tags: - ml x-app: ml x-public: true x-tool: true patch: description: "Applies a JSON merge patch to one of the caller org's deployed models and answers the updated resource\ \ \u2014 the way to change a model's image, replica count or resource requests without tearing the deployment down.\n\ \nThe body is relayed to Kubernetes VERBATIM. That is deliberate and it is why this route is not a typed op: re-encoding\ \ a merge patch changes what it means, because an integer that round-trips through a generic decoder comes back a\ \ float. Merge-patch semantics apply as written \u2014 a null removes a field, and a list is replaced whole rather\ \ than merged.\n\nScoped to the caller's own tenant namespace, resolved from the validated org and project; a name\ \ the caller's tenant does not hold is a 404, never another tenant's resource. An empty body is refused, and a patch\ \ Kubernetes rejects comes back 422 with its reason rather than being silently dropped." operationId: patch_ml_models_by_name parameters: - in: path name: name required: true schema: type: string summary: Change a deployed model in place tags: - ml x-app: ml x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/ml/models/{name}/predict: post: description: "Sends the request body to the named model's predictor and answers the predictor's reply \u2014 its status\ \ code, its body bytes and its Content-Type, all unchanged. This is the inference call itself, not a description of\ \ one.\n\nVERBATIM IS THE CONTRACT, and it is why this route is not a typed op: a model-side error has to surface\ \ as the model's own error, not as this layer's paraphrase of it. The body shape is the kserve v2 inference protocol's,\ \ which means the runtime decides it, not this API. The v2 model name defaults to the resource name \u2014 kserve's\ \ single-model convention \u2014 and a multi-model runtime selects one with the `model` query parameter.\n\nA model\ \ that exists but has no serving address yet answers 503 'not ready' rather than a confusing connection error: deployed\ \ is not the same as serving. Scoped to the caller's own tenant namespace from the validated org and project, so a\ \ name another tenant owns is simply a 404. The predictor's response body is read up to a fixed ceiling." operationId: post_ml_models_by_name_predict parameters: - in: path name: name required: true schema: type: string summary: Run inference against one of your deployed models tags: - ml x-app: ml x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/models: get: description: "Returns the list of available models from the routing table.\n\nPUBLIC BY DESIGN, AND IT DOES NOT AUTHENTICATE\ \ \u2014 that is the whole contract, so it\nis stated here rather than left to be inferred. The catalogue is the same\ \ for\neveryone (listAvailableModels takes no principal), docs.hanzo.ai fetches it from\nthe browser, and every policy\ \ layer around it already says so out loud: the authz\nfilter lists \"models\" as public, filter_balance refuses to\ \ gate it (a 402 here was\na console-wide outage), the rate limiter excludes it, and cloud's spend.Reachable\ncarries\ \ /v1/models/ as \"the model catalog the shell reads for discovery\".\n\nSO THE Authorization HEADER IS NOT AN ADMISSION\ \ CHECK HERE. It is read for ONE\nthing \u2014 annotating gated SKUs with the caller's own access standing \u2014\ \ and\nannotation degrades to nothing when there is no verified principal.\n\nIt used to hold a \"require authentication\"\ \ gate that authenticated nobody: it\nrejected an ABSENT credential and a MALFORMED one, then accepted any string\ \ that\nmerely looked like a key. `Bearer sk-` followed by 36 zeroes returned 200 in\nproduction; so did a JWT three\ \ days expired. It was a shape check wearing an auth\ncheck's clothes, and its cost was diagnostic: /v1/models is\ \ the natural \"is my auth\nworking?\" probe, and answering 200 to a dead credential sent people debugging the\nwrong\ \ system. A public endpoint must not appear to validate. Either check the\ncredential or ignore it \u2014 this one\ \ ignores it, deliberately and visibly.\n\nRemoving that gate discloses nothing new: the catalogue was already reachable\ \ by\nanyone willing to type three characters, so there is no confidentiality delta, only\nan honesty one." operationId: get_models responses: '200': content: application/json: schema: $ref: '#/components/schemas/ai.ModelList' description: Success. '401': description: No credential, or one this service does not accept. '403': description: A valid credential that may not do this. summary: Returns the list of available models from the routing table. tags: - ai x-app: ai x-public: true /v1/models/providers: get: description: "Public, secret-free list of the providers serving the models that\nGET /v1/models lists \u2014 the same\ \ source, projected. Safe unauthenticated: no\nkeys, URLs, or config are returned, and it reports a SET of names,\ \ never\nwhich provider serves which model." operationId: get_models_providers responses: '200': content: application/json: schema: allOf: - $ref: '#/components/schemas/Envelope' - properties: data: $ref: '#/components/schemas/ai.ModelProviders' type: object description: Success. '401': description: No credential, or one this service does not accept. '403': description: A valid credential that may not do this. summary: "Public, secret-free list of the providers serving the models that GET /v1/models lists \u2014 the same source,\ \ projected." tags: - ai x-app: ai x-public: true /v1/models/{model}/access: get: description: 'Returns the caller''s own standing for a gated model: "granted", "requested", or empty when they have never asked.' operationId: get_models_by_model_access parameters: - in: path name: model required: true schema: type: string responses: '200': content: application/json: schema: allOf: - $ref: '#/components/schemas/Envelope' - properties: data: additionalProperties: type: string type: object type: object description: Success. '401': description: No credential, or one this service does not accept. '403': description: A valid credential that may not do this. summary: 'Returns the caller''s own standing for a gated model: "granted", "requested", or empty when they have never asked.' tags: - ai x-app: ai x-public: true post: description: 'Records the caller''s waitlist request for a gated model and answers their new standing. Authed, idempotent, and self-scoped: the row is keyed to the caller''s own org and identity, never to a body-supplied owner.' operationId: post_models_by_model_access parameters: - in: path name: model required: true schema: type: string responses: '200': content: application/json: schema: allOf: - $ref: '#/components/schemas/Envelope' - properties: data: $ref: '#/components/schemas/ModelAccess' type: object description: Success. '401': description: No credential, or one this service does not accept. '403': description: A valid credential that may not do this. summary: Records the caller's waitlist request for a gated model and answers their new standing. tags: - ai x-app: ai x-public: true /v1/mq/health: get: description: Reports whether the message plane behind this surface answers. operationId: get_mq_health responses: '200': content: application/json: schema: $ref: '#/components/schemas/mq.Health' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports whether the message plane behind this surface answers. tags: - mq x-app: mq x-public: true x-tool: true /v1/mq/info: get: description: Returns the broker's identity and the org's stream count. operationId: get_mq_info responses: '200': content: application/json: schema: $ref: '#/components/schemas/mq.infoOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the broker's identity and the org's stream count. tags: - mq x-app: mq x-public: true x-tool: true /v1/mq/stream: get: description: Returns the org's streams, name-ordered, with their live state. operationId: get_mq_stream parameters: - description: "Limit caps the streams returned (1\u20131000, default 100)." in: query name: limit required: false schema: format: int64 type: integer - description: Offset skips that many streams, name-ordered. in: query name: offset required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/mq.Streams' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the org's streams, name-ordered, with their live state. tags: - mq x-app: mq x-public: true x-tool: true post: description: Creates a durable stream in the org's namespace and returns it. operationId: post_mq_stream requestBody: content: application/json: schema: $ref: '#/components/schemas/mq.Config' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/mq.Stream' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a durable stream in the org's namespace and returns it. tags: - mq x-app: mq x-public: true x-tool: true /v1/mq/stream/{name}: delete: description: Removes a stream with all its messages and consumers. Irreversible. operationId: delete_mq_stream_by_name parameters: - description: Name is the stream name, from the path. in: path name: name required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a stream with all its messages and consumers. tags: - mq x-app: mq x-public: true x-tool: true get: description: Returns one stream's configuration and live state. operationId: get_mq_stream_by_name parameters: - description: Name is the stream name, from the path. in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/mq.Stream' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one stream's configuration and live state. tags: - mq x-app: mq x-public: true x-tool: true put: description: 'Reconfigures an existing stream; the path names the stream, and the immutable fields (storage, retention) must restate what they are.' operationId: put_mq_stream_by_name parameters: - description: Name is the stream name, unique within the org (alphanumeric, hyphens, underscores). in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/mq.Config' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/mq.Stream' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reconfigures an existing stream; the path names the stream, and the immutable fields (storage, retention) must restate what they are. tags: - mq x-app: mq x-public: true x-tool: true /v1/mq/stream/{name}/message: get: description: 'Reads stored messages without a consumer: by sequence, by newest on a subject, or walking a subject forward from a sequence.' operationId: get_mq_stream_by_name_message parameters: - description: Name is the stream name, from the path. in: path name: name required: true schema: type: string - description: 'Seq reads the message at this sequence (with next_by_subject: the walk''s start).' in: query name: seq required: false schema: format: uint64 type: integer - description: LastBySubject reads the newest message on this org-relative subject. in: query name: last_by_subject required: false schema: type: string - description: NextBySubject walks forward from seq collecting messages on this org-relative subject (wildcards supported). in: query name: next_by_subject required: false schema: type: string - description: "Limit caps a next_by_subject walk (1\u20131000, default 100)." in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/mq.readOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Reads stored messages without a consumer: by sequence, by newest on a subject, or walking a subject forward from a sequence.' tags: - mq x-app: mq x-public: true x-tool: true /v1/mq/stream/{name}/message/{seq}: delete: description: Erases one message by sequence; the sequence gap remains. operationId: delete_mq_stream_by_name_message_by_seq parameters: - description: Name is the stream name, from the path. in: path name: name required: true schema: type: string - description: Seq is the message's stream sequence, from the path. in: path name: seq required: true schema: format: uint64 type: integer responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Erases one message by sequence; the sequence gap remains. tags: - mq x-app: mq x-public: true x-tool: true /v1/mq/stream/{name}/purge: post: description: Removes messages from a stream, leaving its consumers in place. operationId: post_mq_stream_by_name_purge parameters: - description: Name is the stream name, from the path. in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/mq.Purge' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/mq.purgeOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes messages from a stream, leaving its consumers in place. tags: - mq x-app: mq x-public: true x-tool: true /v1/mq/stream/{stream}/consumer: get: description: Returns a stream's consumers, name-ordered, with delivery state. operationId: get_mq_stream_by_stream_consumer parameters: - description: Stream is the stream name, from the path. in: path name: stream required: true schema: type: string - description: "Limit caps the consumers returned (1\u20131000, default 100)." in: query name: limit required: false schema: format: int64 type: integer - description: Offset skips that many consumers, name-ordered. in: query name: offset required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/mq.pickOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns a stream's consumers, name-ordered, with delivery state. tags: - mq x-app: mq x-public: true x-tool: true post: description: Creates a durable pull consumer on a stream and returns it. operationId: post_mq_stream_by_stream_consumer parameters: - description: Stream is the stream name, from the path. in: path name: stream required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/mq.makeIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/mq.Consumer' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a durable pull consumer on a stream and returns it. tags: - mq x-app: mq x-public: true x-tool: true /v1/mq/stream/{stream}/consumer/{name}: delete: description: 'Removes a consumer and its delivery state; unacknowledged messages stay in the stream.' operationId: delete_mq_stream_by_stream_consumer_by_name parameters: - description: Stream is the stream name, from the path. in: path name: stream required: true schema: type: string - description: Name is the consumer name, from the path. in: path name: name required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a consumer and its delivery state; unacknowledged messages stay in the stream. tags: - mq x-app: mq x-public: true x-tool: true get: description: Returns one consumer's configuration and delivery state. operationId: get_mq_stream_by_stream_consumer_by_name parameters: - description: Stream is the stream name, from the path. in: path name: stream required: true schema: type: string - description: Name is the consumer name, from the path. in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/mq.Consumer' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one consumer's configuration and delivery state. tags: - mq x-app: mq x-public: true x-tool: true /v1/mq/stream/{stream}/consumer/{name}/next: post: description: "Pulls the consumer's next batch. Delivered messages are acknowledged on\ndelivery \u2014 the broker will\ \ not redeliver what this call returns; an empty\nwait answers 408." operationId: post_mq_stream_by_stream_consumer_by_name_next parameters: - description: Stream is the stream name, from the path. in: path name: stream required: true schema: type: string - description: Name is the consumer name, from the path. in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/mq.nextIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/mq.readOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Pulls the consumer's next batch. tags: - mq x-app: mq x-public: true x-tool: true /v1/network: get: description: 'Returns the caller''s org overlay network on the Zero Trust fabric. The org has at most ONE overlay, projected from the edge-routers tagged with its "org-" role attribute: nodes is the real router count and status is "connected" once at least one router has dialed home, "provisioning" while none has. An org with no routers gets an empty list, never a fabricated network. The read degrades rather than erroring: a deployment with no ZT credential, and a controller that cannot be reached, both answer 200 with an empty list so the console''s Networks page renders a clean empty state instead of an error.' operationId: get_network responses: '200': content: application/json: schema: $ref: '#/components/schemas/network.networkList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the caller's org overlay network on the Zero Trust fabric. tags: - network x-app: network x-public: true x-tool: true /v1/network/identities: get: description: 'Returns the fabric identities the caller''s org owns. One row per identity tagged with the org''s "org-" role attribute, each naming the IAM subject it logs in as. A steward sees every one; a plain member sees only its own, since who else is on the org''s network is not a member''s to read, and a plain member''s API key, which holds no identity, is refused. A tenancy read over the full inventory, so like the mesh list it does NOT degrade: an unconfigured deployment answers 503.' operationId: get_network_identities responses: '200': content: application/json: schema: $ref: '#/components/schemas/network.identityList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the fabric identities the caller's org owns. tags: - network x-app: network x-public: true x-tool: true post: description: "Puts the caller on the org's overlay as its own IAM subject.\n\nThe identity is the one whose externalId\ \ is the caller's `sub`, admitted by\nthe controller's \"iam\" auth policy: the caller logs in to the fabric with\ \ its\nown IAM access token, and nothing is enrolled. It is named by the subject\nunless a name is given, and carries\ \ the org's \"org-\" role attribute plus\nany supplied roles \u2014 each scoped to the org, and a \"-host\"\ \ role\nrefused unless the org has published that service. Roles are a steward's to\ntake: a plain member joins with\ \ the org role alone, and asking for more is\n403 before the controller is written.\n\nAn API key is refused 403:\ \ the fabric admits an IAM access token, a person's\nor an application's, and a key has none to log in with.\n\nIDEMPOTENT:\ \ a caller who already has an identity gets the same one back, with\nany of these roles it lacked added \u2014 which\ \ is how one person's identity comes\nto serve every org they act in.\n\nA write, so it does not degrade: an unconfigured\ \ deployment answers 503." operationId: post_network_identities requestBody: content: application/json: schema: $ref: '#/components/schemas/network.identityIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/network.identityView' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Puts the caller on the org's overlay as its own IAM subject. tags: - network x-app: network x-public: true x-tool: true /v1/network/identities/{id}: delete: description: "Takes one of the org's fabric identities out of the org.\n\nThe identity's \"org-\" role and every\ \ role scoped to the org are\nremoved and the rest is left alone, so an identity another org shares keeps\nworking\ \ there. The identity itself is deleted when no org role is left on\nit, or when the caller IS its IAM subject \u2014\ \ a person may always remove\nthemselves.\n\nAn id belonging to another org \u2014 or to nothing \u2014 is 404 before\ \ any write\nreaches the controller: whether an identity exists is itself a cross-tenant\nfact, and a delete may only\ \ ever act on what the caller could list, which for\na plain member is its own identity. A fabric administrator is\ \ the\ncontroller's own and is refused." operationId: delete_network_identities_by_id parameters: - description: 'ID is the identity id from the path. The URL is the addressing authority, so it binds from there whatever else the request carries.' in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Takes one of the org's fabric identities out of the org. tags: - network x-app: network x-public: true x-tool: true /v1/network/routers: get: description: "Returns the Zero Trust routers the caller's org owns.\n\nOne row per real ZT edge-router tagged with the\ \ org's \"org-\" role attribute,\ncarrying the controller's own health signal: \"online\" when connected, \"\ disabled\"\nwhen administratively disabled, \"offline\" otherwise. region is filled only from a\n\"region-\"\ \ role attribute and omitted when the router carries none, so the\ncolumn renders \"\u2014\" rather than a guess.\n\ \nThe read degrades rather than erroring: a deployment with no ZT credential, and a\ncontroller that cannot be reached,\ \ both answer 200 with an empty list." operationId: get_network_routers responses: '200': content: application/json: schema: $ref: '#/components/schemas/network.routerList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the Zero Trust routers the caller's org owns. tags: - network x-app: network x-public: true x-tool: true /v1/network/services: get: description: 'Returns the Zero Trust edge services the caller''s org owns. One row per real ZT edge service tagged with the org''s "org-" role attribute: mtls is "required" when the service mandates end-to-end encryption and "enabled" otherwise (the fabric always mutually authenticates every link), and status is "active" because a listed service is a configured, dialable entry. A service tagged for another org, or tagged for none, is invisible here. Unlike the network and router reads this does NOT degrade: an unconfigured deployment answers 503 and an unreachable controller surfaces the upstream''s status, so a mesh page never renders "no services" for a fabric it simply could not read.' operationId: get_network_services responses: '200': content: application/json: schema: $ref: '#/components/schemas/network.meshServiceList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the Zero Trust edge services the caller's org owns. tags: - network x-app: network x-public: true x-tool: true post: description: "Puts a name on the org's overlay: a fabric service forwarding\nto host:port on whichever of the org's\ \ devices carries the \"-host\"\nrole, dialable at \"..zt\" by any of the org's identities \u2014\ \ and by\nthe cloud's own, which is what lets a BYO cluster's apiserver be attached to\nthe fleet with a \".zt\" kubeconfig.\n\ \nAnswers 201 with the service and its DNS name. The objects behind it are\ncreated in dependency order and unwound\ \ on failure, so a half-published\nservice never lingers on the fabric. Publishing is a steward's act \u2014 an\n\ admin of the org or its own machine client \u2014 and a plain member is 403.\n\nA write, so it does not degrade: an\ \ unconfigured deployment answers 503." operationId: post_network_services requestBody: content: application/json: schema: $ref: '#/components/schemas/network.serviceIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/network.publishedView' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Puts a name on the org's overlay: a fabric service forwarding to host:port on whichever of the org's devices\ \ carries the \"-host\" role, dialable at \"..zt\" by any of the org's identities \u2014 and by the\ \ cloud's own, which is what lets a BYO cluster's apiserver be attached to the fleet with a \".zt\" kubeconfig." tags: - network x-app: network x-public: true x-tool: true /v1/network/services/{id}: delete: description: "Takes a name off the org's overlay: the two policies, the\nservice and its two configs that publishing\ \ made, in that order, and the\n\"-host\" role from each of the org's identities \u2014 that role exists\nonly\ \ for the bind policy to select, and a role naming no service is one\nthe identities endpoint refuses to write.\n\n\ Only objects carrying this service's own names are deleted, so a policy\nthat selects the service by attribute is\ \ left alone. An id belonging to\nanother org \u2014 or to nothing \u2014 is 404 before any write, as for an identity.\n\ Like publishing it is a steward's act, and a plain member is 403 before the\ncontroller is read." operationId: delete_network_services_by_id parameters: - description: 'ID is the service id from the path. The URL is the addressing authority, so it binds from there whatever else the request carries.' in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Takes a name off the org's overlay: the two policies, the service and its two configs that publishing made,\ \ in that order, and the \"-host\" role from each of the org's identities \u2014 that role exists only for the\ \ bind policy to select, and a role naming no service is one the identities endpoint refuses to write." tags: - network x-app: network x-public: true x-tool: true /v1/network/{id}: get: description: "Returns one overlay network by id, scoped to the caller's org.\n\nThe org has exactly one overlay network\ \ and its id is derived from the org, so\nany other id \u2014 another tenant's, or one that does not exist \u2014\ \ is 404 rather than\na peek across the tenant boundary. An org whose network exists but has no\nedge-routers is 404\ \ too, for the same reason the list is empty: there is no\noverlay until something is on it." operationId: get_network_by_id parameters: - description: 'ID is the network id from the path. The URL is the addressing authority, so it binds from there whatever else the request carries.' in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/network.networkView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one overlay network by id, scoped to the caller's org. tags: - network x-app: network x-public: true x-tool: true /v1/node: get: description: "Returns the caller org's currently connected bot nodes: what each one\ncalls itself, the platform it runs\ \ on, its agent version, when its socket was\nestablished, and the capabilities and commands it reported.\n\nOnly\ \ this org's nodes are listed \u2014 the org is half of every key in the table it\nreads \u2014 and only nodes attached\ \ to THIS replica, because the list is of live\nsockets rather than of registrations. The capability and command lists\ \ are the\nnode's own self-report: useful to show, never load-bearing, because what a node\nmay actually be asked\ \ to do is decided at the socket against the deployment's\nallowlist." operationId: get_node responses: '200': content: application/json: schema: $ref: '#/components/schemas/node.nodesView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the caller org''s currently connected bot nodes: what each one calls itself, the platform it runs on, its agent version, when its socket was established, and the capabilities and commands it reported.' tags: - node x-app: node x-public: true x-tool: true /v1/node/connect: get: description: "Upgrades to a WebSocket and keeps it for the life of the node. cloud writes a challenge frame immediately;\ \ the node answers with a connect frame naming the protocol range it speaks, the role `node`, its own node id, and\ \ the display name, platform, agent version, capabilities and commands it reports for itself. On acceptance the session\ \ is registered, the node appears in this org's node list, and invocations begin arriving as frames on the same connection.\n\ \nThe upgrade needs a validated principal and answers 401 without one. The org is the gateway's verdict \u2014 injected\ \ after IAM validation and after any client copy is stripped \u2014 and is never read from the request itself, because\ \ a caller that could name an org could attach a machine into someone else's tenant.\n\nA request carrying an Origin\ \ header is refused outright. A node is a daemon and a browser has no business here; since no same-origin policy applies\ \ to WebSockets, a page could otherwise ride a signed-in viewer's session into registering a node. Removing the whole\ \ category is the gate, not an allowlist of brand domains. The handshake deadline is one fixed instant rather than\ \ a per-read timer, so a peer cannot hold a pre-handshake socket open indefinitely by sending frames this endpoint\ \ ignores.\n\nTwo things to get right. Everything the node declares about itself \u2014 capabilities, commands, platform\ \ \u2014 is a SELF-REPORT: it is useful to show and never load-bearing, because what the node may actually be asked\ \ to run is decided at this socket against the deployment's allowlist. And a node can only ever answer calls placed\ \ on its own connection: correlation ids are minted under the connection id and checked against it, so naming another\ \ node's in-flight call resolves nothing." operationId: get_node_connect summary: The socket a bot node dials and holds open to become invokable. tags: - node x-app: node x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/node/peer/invoke: post: description: "A machine hop, not a caller-facing route. A node's socket lands on one replica while invocations land\ \ on any, so the replica that took the request forwards it here to the one that actually holds the node, and returns\ \ that answer as its own.\n\nIt authenticates with the shared peer token, compared in constant time, and carries no\ \ user identity at all. That is why the org arrives IN THE BODY here: the forwarding replica already derived it from\ \ a gateway-validated header, so the value is a fact being relayed rather than a claim being made. On any caller-facing\ \ route the same field would be a cross-tenant invoke primitive.\n\nIt fails closed on its own configuration: with\ \ no peer token set, or a half-wired cluster that has presence but no way to forward, it serves 503 and forwards nothing\ \ \u2014 an unauthenticated endpoint that takes an org from a body is precisely the hole. A missing or wrong token\ \ is 403, and the forwarded body is bounded.\n\nTwo things to get right. Its refusals are text/plain rather than the\ \ JSON every zip error uses, so a client decoding them as JSON will fail on the error path only. And an invocation\ \ that RAN but was denied still answers 200 here, carrying a stable error token in the JSON body \u2014 no such node,\ \ timeout, node gone, denied, failed \u2014 which the calling replica maps back onto the status codes a caller sees.\ \ Authorization already ran on this replica at the socket and is deliberately not repeated." operationId: post_node_peer_invoke summary: Replica-to-replica forward of one invocation to the pod holding the node's socket. tags: - node x-app: node x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/node/{id}/invoke: post: description: "Sends {command, params, timeoutMs, idempotencyKey} to the named node and answers with what the node returned:\ \ {ok, payload, code, message}, where payload is the node's own JSON passed through \u2014 cloud routes the call,\ \ it does not interpret the result. A reply that is not valid JSON becomes an empty payload rather than corrupting\ \ the response, which ok and code already qualify.\n\nNeither the node nor the org is a body field: the node is the\ \ path and the org is the caller's validated identity, and a field for either would be a field somebody could set\ \ to a stranger's. A validated principal is required (401 without one), and a node id that belongs to another org\ \ answers exactly like one that does not exist \u2014 not found \u2014 so this cannot be used to probe another tenant's\ \ fleet.\n\nAuthorization happened ONCE, at the socket, on the replica holding that node \u2014 the only place that\ \ knows what the node declared it can do. A node attached to a different replica is reached through the peer forward\ \ and is authorized by the same code with the same session in hand, so a local node and a forwarded one cannot get\ \ different answers. The timeout defaults to 30s and is clamped to 5 minutes, so one request can never pin a node's\ \ socket open indefinitely.\n\nsystem.run is rewritten before dispatch: its approval control fields are re-derived\ \ from the approval record and whatever the caller claimed is discarded, because a caller that could pre-approve itself\ \ is the whole thing approvals exist to prevent. No approval registry is wired today, so an invocation CLAIMING an\ \ approval is refused while an ordinary one is unaffected.\n\nThe one thing to get right: a refusal is a 403 carrying\ \ a DOMAIN body \u2014 {error, code, reason} \u2014 not the flat error envelope the rest of cloud returns, and the\ \ same body comes back whether the pre-flight sanitize refused it or the node's own gate did. Switch on `code`. The\ \ remaining failures are ordinary statuses: the node not answering in time is 504, and a node that disconnected or\ \ could not be reached is 502." operationId: post_node_by_id_invoke parameters: - in: path name: id required: true schema: type: string summary: Ask one of your connected machines to run a command, and get its answer back. tags: - node x-app: node x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/notify/credentials/{provider}/{key}: delete: description: 'Removes one of your org''s notify provider credentials. The value is forgotten in KMS. A provider missing a key it cannot send without is no longer picked for its channel, and a send that pins it fails with the key it lacks. Removing a key that is not set succeeds, since what was asked for is already true. Org admin only.' operationId: delete_notify_credentials_by_provider_by_key parameters: - description: Provider is the delivery provider the credential is for. in: path name: provider required: true schema: type: string - description: Key is the credential's name within that provider. in: path name: key required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one of your org's notify provider credentials. tags: - notify x-app: notify x-public: true x-tool: true put: description: "Sets one of your org's notify provider credentials.\n\nThe value is sealed in KMS under your org and read\ \ by notify alone, at the\nmoment it sends; no route answers it back, this one included. Setting a key\nthat is already\ \ set replaces it, which is how a credential is rotated, and the\nnext send uses the new value. The key must be one\ \ the provider reads \u2014 Twilio's\naccount-sid, auth-token and from-number, say \u2014 and anything else is a 400\n\ naming the keys it does read. An unknown provider is a 404.\n\nOrg admin only: a credential set here is what every\ \ message the org sends\ngoes out with." operationId: put_notify_credentials_by_provider_by_key parameters: - description: 'Provider is the delivery provider the credential is for: twilio, plivo, twilio_email or mail.' example: twilio in: path name: provider required: true schema: type: string - description: 'Key is one of the credentials that provider reads, such as auth-token or smtp-host. A key the provider does not read is refused.' example: auth-token in: path name: key required: true schema: type: string requestBody: content: application/json: example: key: auth-token provider: twilio value: "\u2026" schema: $ref: '#/components/schemas/notify.notifyCredential' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/notify.notifyStored' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Sets one of your org's notify provider credentials. tags: - notify x-app: notify x-public: true x-tool: true /v1/notify/health: get: description: 'Reports that the notify send surface is mounted. It is a pure liveness probe: it answers 200 whenever this subsystem is mounted and checks nothing downstream, so an "ok" here says the routes are reachable, not that any provider credential is configured. The body is notifyd''s verbatim, so probes and clients that keyed on the standalone service keep working unchanged.' operationId: get_notify_health responses: '200': content: application/json: schema: $ref: '#/components/schemas/notify.notifyHealth' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports that the notify send surface is mounted. tags: - notify x-app: notify x-public: true x-tool: true /v1/notify/send: post: description: "Delivers one transactional message by email or SMS through the caller\norg's own provider credential.\n\ \nThe channel comes from the body \u2014 sms or email \u2014 and the provider credential is\nread from KMS at orgs//notify//,\ \ never from the environment.\nThe org is the validated principal's, never a client-supplied value, so a caller\n\ can only ever send as their own tenant; an unauthenticated caller gets 401.\nNaming no provider picks the one whose\ \ credentials are actually configured\n(Twilio, then Plivo for SMS; Twilio Email, then SMTP for email) and fails closed\n\ when none is. Delivery is synchronous and per recipient: one recipient answers\nthe bare {message_id,status} outcome,\ \ several answer the {items:[\u2026]} envelope. A\nterminal provider failure is a 200 whose status is failed with\ \ the reason in\nerror, never a transport error. sync=true is REQUIRED \u2014 an async dispatch\nanswers 503, because\ \ the queue plane that would run it is owned elsewhere. The\nmessage body wins verbatim when present; otherwise template_id\ \ (or the event\nname) selects a built-in template rendered against template_vars." operationId: post_notify_send requestBody: content: application/json: schema: $ref: '#/components/schemas/notify.notifySend' required: true responses: '200': content: application/json: schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Delivers one transactional message by email or SMS through the caller org's own provider credential. tags: - notify x-app: notify x-public: true x-tool: true /v1/notify/send/email: post: description: "Delivers one transactional email through the caller org's own\nprovider credential.\n\nIt is the channel-pinned\ \ form of the generic send: identical in every respect\nexcept that the channel is fixed to email, OVERRIDING whatever\ \ the body names \u2014\nso a body that says sms still goes out as mail. The provider is the org's own\nemail credential\ \ from KMS (Twilio Email, then SMTP), resolved for the validated\nprincipal's org; an unauthenticated caller gets\ \ 401. Subject is carried on the\nemail channel only." operationId: post_notify_send_email requestBody: content: application/json: schema: $ref: '#/components/schemas/notify.notifySend' required: true responses: '200': content: application/json: schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Delivers one transactional email through the caller org's own provider credential. tags: - notify x-app: notify x-public: true x-tool: true /v1/notify/send/sms: post: description: "Delivers one transactional SMS through the caller org's own provider\ncredential.\n\nIt is the channel-pinned\ \ form of the generic send: identical in every respect\nexcept that the channel is fixed to sms, OVERRIDING whatever\ \ the body names \u2014\nso a body that says email still goes out as a text message. The provider is the\norg's own\ \ SMS credential from KMS (Twilio, then Plivo), resolved for the\nvalidated principal's org; an unauthenticated caller\ \ gets 401." operationId: post_notify_send_sms requestBody: content: application/json: schema: $ref: '#/components/schemas/notify.notifySend' required: true responses: '200': content: application/json: schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Delivers one transactional SMS through the caller org's own provider credential. tags: - notify x-app: notify x-public: true x-tool: true /v1/o11y/alerts: get: description: Returns the org's current alerts. Viewer gate. operationId: GetAlerts responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAlertsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the org's current alerts. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/alerts/last: get: description: "Answers the most recent Alertmanager deliveries THIS process received, as plain text \u2014 one greppable\ \ `ALERT-RECEIVED` line per alert, followed by the `ALERT-DELIVERED` / `ALERT-UNDELIVERED` outcome of carrying it\ \ out of the process, newest last, so piping to `tail` reads in arrival order. `(none)` when nothing has landed.\n\ \nArrival and delivery are separate lines because they are separate facts that fail independently. Alertmanager can\ \ tell you it dispatched a notification, never that anything received it; this process taking the call says nothing\ \ about whether a human was reached. Reading only the first as if it were the second is how a pager stays silent for\ \ months behind a log where everything looks fine.\n\nThe ring is PROCESS-LOCAL and bounded to the last 200 lines.\ \ Both are the point: a record that outlived the process that took the call would be a claim about something nobody\ \ observed, and an unbounded log is a memory leak with a nice name. A restart empties it." operationId: get_o11y_alerts_last summary: Replay the alert records this process took tags: - o11y x-app: o11y x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/o11y/alerts/{receiver}: post: description: "Records one Alertmanager webhook delivery and pages the on-call. Each alert prints an `ALERT-RECEIVED`\ \ line and joins the replay ring, then the batch is carried out of the process by the egress chain: the org's KMS-custodied\ \ Slack bot token first (the ONE product Slack egress, not a second webhook credential), then an email to `CLOUD_ALERTS_EMAIL_TO`\ \ on the org's own notify credential, then a plain POST to `CLOUD_ALERTS_WEBHOOK_URL` \u2014 which needs no Slack\ \ connection and so works in exactly the state that silences the first. Resolved notifications page too: \"it recovered\"\ \ is the half of an incident people are actually waiting for.\n\nTHE STATUS CODE REPORTS DELIVERY, NOT ARRIVAL. 200\ \ `ok` means an egress accepted the batch. If none did \u2014 including when none is configured at all \u2014 it answers\ \ **503** naming the failure, so Alertmanager retries and counts it in `alertmanager_notifications_failed_total`.\ \ An alert nobody could be told about must never answer the same way as one that was delivered.\n\nA body that will\ \ not parse is still recorded (with empty fields) rather than rejected: the delivery happened, which is the fact being\ \ recorded, and a 400 would make Alertmanager retry a malformed payload forever.\n\nThe receiver segment is Alertmanager's\ \ own receiver name, a parameter rather than a hand-listed route because the receiver set is config, not code." operationId: post_o11y_alerts_by_receiver parameters: - in: path name: receiver required: true schema: type: string summary: Take an Alertmanager notification and page a human tags: - o11y x-app: o11y x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/o11y/api/{project_id}/envelope: post: description: "Accepts an application/x-sentry-envelope frame from a Sentry SDK \u2014 the batched wire format carrying\ \ events, sessions and attachments \u2014 and ingests it against the project named in the path.\n\nTHE /api/ SEGMENT\ \ IS NOT OURS TO NAME. An SDK appends its own fixed /api//envelope/ suffix to whatever DSN it is given, so\ \ this address is the SDK's, received verbatim. We receive this shape; we do not publish it. The clean spelling of\ \ the same wire is /v1/event/{project}/envelope/.\n\nAUTHENTICATED BY THE DSN PUBLIC KEY, never a Hanzo session, and\ \ therefore exempt from the principal gate: the ingest verifier checks the key in constant time, fails closed, and\ \ derives the org from it. A keyless submission is a 401 from that verifier \u2014 not a 403 from the gate, and not\ \ a 404 \u2014 which is how you tell the hops apart. The exemption is matched by method plus prefix plus suffix, never\ \ a bare prefix, so no read is reachable through it." operationId: post_o11y_api_by_project_id_envelope parameters: - in: path name: project_id required: true schema: type: string summary: Receive a Sentry envelope on the SDK's own DSN path tags: - o11y x-app: o11y x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/o11y/api/{project_id}/store: post: description: "The legacy single-event form of the envelope ingest: one JSON event rather than a framed batch, kept because\ \ SDKs in the field still send it.\n\nSame address ownership and same authentication as the envelope route \u2014\ \ the /api/ segment is the SDK's, the DSN public key is the credential, the principal gate does not apply, and a keyless\ \ submission is a 401 from the ingest verifier." operationId: post_o11y_api_by_project_id_store parameters: - in: path name: project_id required: true schema: type: string summary: Receive a single Sentry event on the SDK's own DSN path tags: - o11y x-app: o11y x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/o11y/authz/check: post: description: "Evaluates a batch of transactions \u2014 relation plus object \u2014 for\nthe authenticated caller and\ \ answers each with its authorization verdict, in\nthe order they were asked. It is the read a UI uses to decide which\ \ controls\nto show." operationId: AuthzCheck requestBody: content: application/json: schema: items: $ref: '#/components/schemas/o11y.O11yTransaction' type: array required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yCheckOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Evaluates a batch of transactions \u2014 relation plus object \u2014 for the authenticated caller and answers\ \ each with its authorization verdict, in the order they were asked." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/auto_complete/attribute_values: post: description: "Reads the attribute-value request from the body rather\nthan off the query string \u2014 the spelling\ \ the newer builder uses to send its\nfilters alongside the request.\n\nCallers need the viewer role; the runtime's\ \ own gate enforces it." operationId: post_o11y_auto_complete_attribute_values requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.FilterAttributeValueRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAttributeValuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Reads the attribute-value request from the body rather than off the query string \u2014 the spelling the newer\ \ builder uses to send its filters alongside the request." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/autocomplete/aggregate_attributes: get: description: "Lists the attributes usable as an aggregate target for\nthe given telemetry and operator \u2014 what a\ \ filter builder offers after the\naggregation is chosen.\n\nCallers need the viewer role; the runtime's own gate\ \ enforces it." operationId: get_o11y_autocomplete_aggregate_attributes parameters: - description: "DataSource is the telemetry the attributes come from \u2014 traces, logs,\nmetrics or meter. The runtime\ \ requires it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the attribute will be used under, e.g. count, avg, sum. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: SearchText narrows the attributes to those containing it. in: query name: searchText required: false schema: type: string - description: Limit caps how many attributes come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAggregateAttributesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the attributes usable as an aggregate target for the given telemetry and operator \u2014 what a filter\ \ builder offers after the aggregation is chosen." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/autocomplete/attribute_keys: get: description: 'Lists the attribute keys available for filtering the given telemetry, each with its data type and whether it is a materialized column. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_autocomplete_attribute_keys parameters: - description: "DataSource is the telemetry the keys come from \u2014 traces, logs, metrics or\nmeter. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the keys will be used under. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the keys must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: SearchText narrows the keys to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the keys to one kind \u2014 tag or resource. Empty means all;\nan invalid value reads\ \ as empty." in: query name: tagType required: false schema: type: string - description: Limit caps how many keys come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAttributeKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the attribute keys available for filtering the given telemetry, each with its data type and whether it is a materialized column. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/autocomplete/attribute_values: get: description: "Lists the values one attribute key has taken \u2014 string,\nnumber and bool values in their own lists\ \ \u2014 for completing a filter.\n\nCallers need the viewer role; the runtime's own gate enforces it." operationId: get_o11y_autocomplete_attribute_values parameters: - description: "DataSource is the telemetry the values come from \u2014 traces, logs or\nmetrics. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the values will be used under. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the values must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: AttributeKey is the key whose values to list. in: query name: attributeKey required: false schema: type: string - description: "FilterAttributeKeyDataType is the key's data type \u2014 string, int64, float64\nor bool. Empty means\ \ unspecified." in: query name: filterAttributeKeyDataType required: false schema: type: string - description: SearchText narrows the values to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the search to one kind of key \u2014 tag or resource." in: query name: tagType required: false schema: type: string - description: Limit caps how many values come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAttributeValuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the values one attribute key has taken \u2014 string, number and bool values in their own lists \u2014\ \ for completing a filter." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/availability: get: description: "Reports how much of the Hanzo fleet is up \u2014 the current\nper-service inventory plus an up-versus-reporting\ \ trend across the window.\nBoth come from the fleet prober's own measurements: every service is asked its\nhealth\ \ URL every 30 seconds, so a service is listed as down because it did not\nanswer, never because something failed\ \ to collect it. PLATFORM SUDO ONLY \u2014\nthis is the whole fleet's inventory, not tenant data, so every customer\ \ is\n403. An unreachable telemetry store answers 503 rather than an empty trend,\nbecause a board of zeroes and a\ \ fleet that is down look identical." operationId: get_o11y_availability parameters: - description: Range is the trend window in seconds. Default 3600, capped at 604800 (7d). example: 3600 in: query name: range required: false schema: format: int64 type: integer - description: 'StepSec is the bucket width in seconds, clamped to [30, 3600]. Absent picks ~60 buckets across the range.' in: query name: stepSec required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.availabilityResponse' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Reports how much of the Hanzo fleet is up \u2014 the current per-service inventory plus an up-versus-reporting\ \ trend across the window." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/channels: get: description: Lists the org's notification channels. Viewer gate. operationId: ListChannels responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yChannelsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the org's notification channels. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Creates a notification channel, answering with the stored channel. Admin gate.' operationId: CreateChannel requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostableChannel' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yChannelOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a notification channel, answering with the stored channel. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/channels/test: post: description: Sends a test notification to the posted receiver. Editor gate. operationId: TestChannel requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.alertmanagertypes.Receiver' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Sends a test notification to the posted receiver. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/channels/{id}: delete: description: Removes a notification channel, by id. Admin gate. operationId: DeleteChannelByID parameters: - in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a notification channel, by id. tags: - o11y x-app: o11y x-public: true x-tool: true get: description: Returns one notification channel, by id. Viewer gate. operationId: GetChannelByID parameters: - in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yChannelOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one notification channel, by id. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: Replaces a notification channel's receiver, by id. Admin gate. operationId: UpdateChannelByID parameters: - in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yChannelUpdateIn' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Replaces a notification channel's receiver, by id. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/cloud-integrations/{cloud_provider}/agent-check-in: post: description: "Is the deployed agent's check-in on its original\nhyphenated path, kept for backward compatibility with\ \ agents already\nrunning. Viewer gate \u2014 the agent's role is viewer." operationId: AgentCheckInDeprecated parameters: - in: path name: cloud_provider required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAgentCheckInIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAgentCheckInOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Is the deployed agent's check-in on its original hyphenated path, kept for backward compatibility with agents already running. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/cloud_integrations/{cloud_provider}/accounts: get: description: 'Lists the cloud-integration accounts connected for the given provider. Admin gate.' operationId: ListAccounts parameters: - in: path name: cloud_provider required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAccountsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the cloud-integration accounts connected for the given provider. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Connects a new cloud-integration account for the given provider from its posted config and credentials, answering with the account and the artifact the agent deploys to complete the connection. Admin gate.' operationId: CreateAccount parameters: - in: path name: cloud_provider required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yCreateAccountIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yCreateAccountOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Connects a new cloud-integration account for the given provider from its posted config and credentials, answering with the account and the artifact the agent deploys to complete the connection. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/cloud_integrations/{cloud_provider}/accounts/check_in: post: description: "Is the deployed agent's check-in \u2014 the path consistent with the\naccount surface, reporting the agent's\ \ account and telemetry state so the\nconnection can be tracked. Viewer gate \u2014 the agent's role is viewer." operationId: AgentCheckIn parameters: - in: path name: cloud_provider required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAgentCheckInIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAgentCheckInOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Is the deployed agent's check-in \u2014 the path consistent with the account surface, reporting the agent's\ \ account and telemetry state so the connection can be tracked." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/cloud_integrations/{cloud_provider}/accounts/{id}: delete: description: 'Tears down a connected account for the given provider, by id. Admin gate.' operationId: DisconnectAccount parameters: - in: path name: cloud_provider required: true schema: type: string - in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Tears down a connected account for the given provider, by id. tags: - o11y x-app: o11y x-public: true x-tool: true get: description: 'Returns one connected account for the given provider, by id. Admin gate.' operationId: GetAccount parameters: - in: path name: cloud_provider required: true schema: type: string - in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAccountOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one connected account for the given provider, by id. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: 'Changes a connected account''s configuration for the given provider, by id. Admin gate.' operationId: UpdateAccount parameters: - in: path name: cloud_provider required: true schema: type: string - in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yUpdateAccountIn' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes a connected account's configuration for the given provider, by id. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/cloud_integrations/{cloud_provider}/accounts/{id}/services: get: description: 'Lists the services metadata for one connected account of the given provider, by account id. Admin gate.' operationId: ListAccountServicesMetadata parameters: - in: path name: cloud_provider required: true schema: type: string - in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yServicesMetadataOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the services metadata for one connected account of the given provider, by account id. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/cloud_integrations/{cloud_provider}/accounts/{id}/services/{service_id}: get: description: 'Returns one service and its configuration for a connected account of the given provider, by account id and service id. Admin gate.' operationId: GetAccountService parameters: - in: path name: cloud_provider required: true schema: type: string - in: path name: id required: true schema: type: string - in: path name: service_id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yServiceOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one service and its configuration for a connected account of the given provider, by account id and service id. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: 'Changes a service''s configuration for one connected account of the given provider, by account id and service id. Admin gate.' operationId: UpdateService parameters: - in: path name: cloud_provider required: true schema: type: string - in: path name: id required: true schema: type: string - in: path name: service_id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yUpdateServiceIn' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes a service's configuration for one connected account of the given provider, by account id and service id. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/cloud_integrations/{cloud_provider}/credentials: get: description: 'Returns the credentials the connecting agent needs to establish the cloud integration, for the given cloud provider. Admin gate.' operationId: GetConnectionCredentials parameters: - in: path name: cloud_provider required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yCredentialsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the credentials the connecting agent needs to establish the cloud integration, for the given cloud provider. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/cloud_integrations/{cloud_provider}/services: get: description: 'Lists the services the given provider can collect from, optionally scoped to one cloud integration. Admin gate.' operationId: ListServicesMetadata parameters: - in: path name: cloud_provider required: true schema: type: string - description: CloudIntegrationID, when set, scopes the listing to one cloud integration. in: query name: cloud_integration_id required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yServicesMetadataOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the services the given provider can collect from, optionally scoped to one cloud integration. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/cloud_integrations/{cloud_provider}/services/{service_id}: get: description: 'Returns one service the given provider can collect from, by service id, optionally scoped to one cloud integration. Admin gate.' operationId: GetService parameters: - in: path name: cloud_provider required: true schema: type: string - in: path name: service_id required: true schema: type: string - description: CloudIntegrationID, when set, scopes the service to one cloud integration. in: query name: cloud_integration_id required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yServiceOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one service the given provider can collect from, by service id, optionally scoped to one cloud integration. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/clusters/attribute_keys: get: description: 'Lists the metric attribute keys Kubernetes clusters report, for building cluster filters.' operationId: get_o11y_clusters_attribute_keys parameters: - description: "DataSource is the telemetry the keys come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the keys will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the keys must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: SearchText narrows the keys to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the keys to one kind \u2014 tag or resource. Empty means all;\nan invalid value reads\ \ as empty." in: query name: tagType required: false schema: type: string - description: Limit caps how many keys come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the metric attribute keys Kubernetes clusters report, for building cluster filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/clusters/attribute_values: get: description: 'Lists the values one cluster attribute key has taken, for building cluster filters.' operationId: get_o11y_clusters_attribute_values parameters: - description: "DataSource is the telemetry the values come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the values will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the values must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: AttributeKey is the key whose values to list. in: query name: attributeKey required: false schema: type: string - description: "FilterAttributeKeyDataType is the key's data type \u2014 string, int64,\nfloat64 or bool. Empty means\ \ unspecified." in: query name: filterAttributeKeyDataType required: false schema: type: string - description: SearchText narrows the values to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the search to one kind of key \u2014 tag or resource." in: query name: tagType required: false schema: type: string - description: Limit caps how many values come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeValuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the values one cluster attribute key has taken, for building cluster filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/clusters/list: post: description: 'Lists Kubernetes clusters over a time range, each with its CPU and memory usage against allocatable capacity and its attributes; filterable, groupable and paginated.' operationId: post_o11y_clusters_list requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.ClusterListRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yClusterListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes clusters over a time range, each with its CPU and memory usage against allocatable capacity and its attributes; filterable, groupable and paginated. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/countErrors: post: description: 'Counts the grouped exceptions in the query window for the caller''s org. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: post_o11y_counterrors requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yErrorsCountIn' required: true responses: '200': content: application/json: schema: format: uint64 type: integer description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Counts the grouped exceptions in the query window for the caller's org. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/daemonsets/attribute_keys: get: description: 'Lists the metric attribute keys Kubernetes daemonsets report, for building daemonset filters.' operationId: get_o11y_daemonsets_attribute_keys parameters: - description: "DataSource is the telemetry the keys come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the keys will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the keys must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: SearchText narrows the keys to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the keys to one kind \u2014 tag or resource. Empty means all;\nan invalid value reads\ \ as empty." in: query name: tagType required: false schema: type: string - description: Limit caps how many keys come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the metric attribute keys Kubernetes daemonsets report, for building daemonset filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/daemonsets/attribute_values: get: description: 'Lists the values one daemonset attribute key has taken, for building daemonset filters.' operationId: get_o11y_daemonsets_attribute_values parameters: - description: "DataSource is the telemetry the values come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the values will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the values must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: AttributeKey is the key whose values to list. in: query name: attributeKey required: false schema: type: string - description: "FilterAttributeKeyDataType is the key's data type \u2014 string, int64,\nfloat64 or bool. Empty means\ \ unspecified." in: query name: filterAttributeKeyDataType required: false schema: type: string - description: SearchText narrows the values to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the search to one kind of key \u2014 tag or resource." in: query name: tagType required: false schema: type: string - description: Limit caps how many values come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeValuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the values one daemonset attribute key has taken, for building daemonset filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/daemonsets/list: post: description: 'Lists Kubernetes daemonsets over a time range, each with the CPU and memory its pods used against request and limit, desired and available node counts, restarts and attributes; filterable, groupable and paginated.' operationId: post_o11y_daemonsets_list requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.DaemonSetListRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDaemonSetListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes daemonsets over a time range, each with the CPU and memory its pods used against request and limit, desired and available node counts, restarts and attributes; filterable, groupable and paginated. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/dashboard_views: get: description: 'Returns every saved view in the calling user''s org. Saved views are shared org-wide. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: ListDashboardViews responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardViewListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns every saved view in the calling user's org. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Persists the calling user''s dashboard-listing state (query, sort, order) as a named, reusable view shared across the org. Callers need the editor role; the runtime''s own gate enforces it.' operationId: CreateDashboardView requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardViewPostable' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardViewOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Persists the calling user's dashboard-listing state (query, sort, order) as a named, reusable view shared across the org. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/dashboard_views/{id}: delete: description: 'Removes a saved view. Saved views are shared org-wide. Deleting a non-existent view refuses with the runtime''s not-found. Callers need the editor role; the runtime''s own gate enforces it.' operationId: DeleteDashboardView parameters: - description: ID is the resource id from the path. in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a saved view. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: 'Replaces a saved view''s name and data. Saved views are shared org-wide. Callers need the editor role; the runtime''s own gate enforces it.' operationId: UpdateDashboardView parameters: - description: ID is the saved view id from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardViewUpdateIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardViewOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Replaces a saved view's name and data. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/dashboards: get: description: "Returns a page of v2-shape dashboards for the org. This is the\npure, user-independent list \u2014 it\ \ carries no pin state; use\ndashboardListForUserV2 for the personalized, pin-aware list. Supports a filter\nDSL (query),\ \ sort (updated_at/created_at/name), order (asc/desc), and\noffset-based pagination (limit/offset).\n\nCallers need\ \ the viewer role; the runtime's own gate enforces it." operationId: ListDashboardsV2 parameters: - description: 'Query is the filter DSL over dashboard columns and tags, e.g. `name:cpu source:user`. Empty lists everything.' in: query name: query required: false schema: type: string - description: 'Sort is the sort field: updated_at, created_at or name. Empty sorts by updated_at.' in: query name: sort required: false schema: type: string - description: 'Order is the sort direction: asc or desc. Empty orders desc.' in: query name: order required: false schema: type: string - description: 'Limit caps how many dashboards come back. Zero means the default of 20; the runtime caps it at 200.' in: query name: limit required: false schema: format: int64 type: integer - description: Offset is how many dashboards to skip for pagination. in: query name: offset required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns a page of v2-shape dashboards for the org. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Creates a dashboard in the v2 format that follows the Perses spec and answers with the stored dashboard. Callers need the editor role; the runtime''s own gate enforces it.' operationId: CreateDashboardV2 requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardPostable' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a dashboard in the v2 format that follows the Perses spec and answers with the stored dashboard. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/dashboards/{id}: delete: description: 'Deletes a v2-shape dashboard along with its tag relations. Locked dashboards are rejected. Callers need the editor role; the runtime''s own gate enforces it.' operationId: DeleteDashboardV2 parameters: - description: ID is the resource id from the path. in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes a v2-shape dashboard along with its tag relations. tags: - o11y x-app: o11y x-public: true x-tool: true get: description: 'Returns a v2-shape dashboard. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: GetDashboardV2 parameters: - description: ID is the resource id from the path. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns a v2-shape dashboard. tags: - o11y x-app: o11y x-public: true x-tool: true patch: description: "Applies an RFC 6902 JSON Patch to a v2-shape dashboard. The\npatch is applied against the postable view\ \ (metadata, spec, tags), so individual\npanels, queries, variables, layouts or tags can be updated without re-sending\ \ the\nrest. Apply is lenient \u2014 remove on a missing path is a no-op and add creates any\nmissing parent objects\ \ \u2014 and the result is still validated. Locked dashboards are\nrejected. The request body is the bare JSON Patch\ \ operations array.\n\nCallers need the editor role; the runtime's own gate enforces it." operationId: PatchDashboardV2 parameters: - description: ID is the dashboard id from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardPatchIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Applies an RFC 6902 JSON Patch to a v2-shape dashboard. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: 'Updates a v2-shape dashboard''s metadata, spec and tag set. The name is immutable and locked dashboards are rejected. Callers need the editor role; the runtime''s own gate enforces it.' operationId: UpdateDashboardV2 parameters: - description: ID is the dashboard id from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardUpdateIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Updates a v2-shape dashboard's metadata, spec and tag set. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/dashboards/{id}/clone: post: description: 'Clones an existing v2-shape dashboard. User and integration dashboards can be cloned; system dashboards are rejected. The clone keeps the source''s display name, panels and tags, but gets a freshly generated unique internal name and is always created as an unlocked user dashboard owned by the caller. Callers need the editor role; the runtime''s own gate enforces it.' operationId: CloneDashboardV2 parameters: - description: ID is the resource id from the path. in: path name: id required: true schema: type: string responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Clones an existing v2-shape dashboard. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/dashboards/{id}/lock: delete: description: 'Unlocks a v2-shape dashboard. Only the dashboard''s creator or an org admin may lock or unlock. Callers need the editor role; the runtime''s own gate enforces it.' operationId: UnlockDashboardV2 parameters: - description: ID is the resource id from the path. in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Unlocks a v2-shape dashboard. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: 'Locks a v2-shape dashboard. Only the dashboard''s creator or an org admin may lock or unlock. Callers need the editor role; the runtime''s own gate enforces it.' operationId: LockDashboardV2 parameters: - description: ID is the resource id from the path. in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Locks a v2-shape dashboard. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/dashboards/{id}/public: delete: description: 'Deletes the public-sharing config and disables public sharing of a dashboard. Callers need the admin role; the runtime''s own gate enforces it.' operationId: DeletePublicDashboard parameters: - description: ID is the resource id from the path. in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes the public-sharing config and disables public sharing of a dashboard. tags: - o11y x-app: o11y x-public: true x-tool: true get: description: 'Returns the public-sharing config for a dashboard. Callers need the admin role; the runtime''s own gate enforces it.' operationId: GetPublicDashboard parameters: - description: ID is the resource id from the path. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yPublicDashboardOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the public-sharing config for a dashboard. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Creates the public-sharing config for a dashboard and enables public sharing, answering with the new share''s id. Callers need the admin role; the runtime''s own gate enforces it.' operationId: CreatePublicDashboard parameters: - description: ID is the dashboard id from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yPublicDashboardWriteIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yIdentifiableOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates the public-sharing config for a dashboard and enables public sharing, answering with the new share's id. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: 'Updates the public-sharing config for a dashboard. Callers need the admin role; the runtime''s own gate enforces it.' operationId: UpdatePublicDashboard parameters: - description: ID is the dashboard id from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yPublicDashboardWriteIn' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Updates the public-sharing config for a dashboard. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/dependency_graph: post: description: "Returns the service dependency graph over the requested\nwindow: every parent\u2192child edge observed,\ \ with call and error rates and\nlatency percentiles per edge.\n\nCallers need the viewer role; the runtime's own\ \ gate enforces it." operationId: post_o11y_dependency_graph requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDependencyGraphIn' required: true responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/o11y.O11yDependency' type: array description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the service dependency graph over the requested window: every parent\u2192child edge observed, with\ \ call and error rates and latency percentiles per edge." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/deployments/attribute_keys: get: description: 'Lists the metric attribute keys Kubernetes deployments report, for building deployment filters.' operationId: get_o11y_deployments_attribute_keys parameters: - description: "DataSource is the telemetry the keys come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the keys will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the keys must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: SearchText narrows the keys to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the keys to one kind \u2014 tag or resource. Empty means all;\nan invalid value reads\ \ as empty." in: query name: tagType required: false schema: type: string - description: Limit caps how many keys come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the metric attribute keys Kubernetes deployments report, for building deployment filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/deployments/attribute_values: get: description: 'Lists the values one deployment attribute key has taken, for building deployment filters.' operationId: get_o11y_deployments_attribute_values parameters: - description: "DataSource is the telemetry the values come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the values will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the values must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: AttributeKey is the key whose values to list. in: query name: attributeKey required: false schema: type: string - description: "FilterAttributeKeyDataType is the key's data type \u2014 string, int64,\nfloat64 or bool. Empty means\ \ unspecified." in: query name: filterAttributeKeyDataType required: false schema: type: string - description: SearchText narrows the values to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the search to one kind of key \u2014 tag or resource." in: query name: tagType required: false schema: type: string - description: Limit caps how many values come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeValuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the values one deployment attribute key has taken, for building deployment filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/deployments/list: post: description: 'Lists Kubernetes deployments over a time range, each with the CPU and memory its pods used against request and limit, desired and available replica counts, restarts and attributes; filterable, groupable and paginated.' operationId: post_o11y_deployments_list requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.DeploymentListRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDeploymentListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes deployments over a time range, each with the CPU and memory its pods used against request and limit, desired and available replica counts, restarts and attributes; filterable, groupable and paginated. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/disks: get: description: 'Lists the storage disks the datastore reports, with their names and types. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_disks responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/o11y.O11yDisk' type: array description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the storage disks the datastore reports, with their names and types. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/downtime_schedules: get: description: 'Lists all planned maintenance windows, optionally narrowed to the active ones or the recurring ones. Viewer gate.' operationId: ListDowntimeSchedules parameters: - description: 'Active, when "true" or "false", keeps only the active or inactive windows. Absent lists all.' in: query name: active required: false schema: type: string - description: 'Recurring, when "true" or "false", keeps only the recurring or one-off windows. Absent lists all.' in: query name: recurring required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDowntimeSchedulesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists all planned maintenance windows, optionally narrowed to the active ones or the recurring ones. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Creates a planned maintenance window, answering with the stored schedule. Editor gate.' operationId: CreateDowntimeSchedule requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostablePlannedMaintenance' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDowntimeScheduleOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a planned maintenance window, answering with the stored schedule. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/downtime_schedules/{id}: delete: description: Removes a planned maintenance window, by id. Editor gate. operationId: DeleteDowntimeScheduleByID parameters: - in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a planned maintenance window, by id. tags: - o11y x-app: o11y x-public: true x-tool: true get: description: Returns one planned maintenance window, by id. Viewer gate. operationId: GetDowntimeScheduleByID parameters: - in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDowntimeScheduleOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one planned maintenance window, by id. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: Replaces a planned maintenance window, by id. Editor gate. operationId: UpdateDowntimeScheduleByID parameters: - in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDowntimeUpdateIn' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Replaces a planned maintenance window, by id. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/errorFromErrorID: get: description: 'Returns one exception instance and the span it happened on, by its error id within a group at a timestamp. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_errorfromerrorid parameters: - description: 'Timestamp is the instance''s time as a nanosecond epoch spelled as a string. Required.' in: query name: timestamp required: true schema: type: string - description: GroupID is the exception group the instance belongs to. Required. in: query name: groupID required: true schema: type: string - description: 'ErrorID is the exception instance id. Required by errorFromErrorID and nextPrevErrorIDs; unused by errorFromGroupID.' in: query name: errorID required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yErrorWithSpan' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one exception instance and the span it happened on, by its error id within a group at a timestamp. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/errorFromGroupID: get: description: 'Returns the representative exception instance of a group at a timestamp, and the span it happened on. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_errorfromgroupid parameters: - description: 'Timestamp is the instance''s time as a nanosecond epoch spelled as a string. Required.' in: query name: timestamp required: true schema: type: string - description: GroupID is the exception group the instance belongs to. Required. in: query name: groupID required: true schema: type: string - description: 'ErrorID is the exception instance id. Required by errorFromErrorID and nextPrevErrorIDs; unused by errorFromGroupID.' in: query name: errorID required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yErrorWithSpan' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the representative exception instance of a group at a timestamp, and the span it happened on. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/errortracking/issues: get: description: 'Lists the caller''s org''s grouped error issues (by fingerprint) with status, level, counts and first/last-seen. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_errortracking_issues parameters: - description: 'Status narrows to one lifecycle state: unresolved, resolved or ignored.' in: query name: status required: false schema: type: string - description: Level narrows to one severity, e.g. error, warning, info. in: query name: level required: false schema: type: string - description: Environment narrows to one deployment environment. in: query name: environment required: false schema: type: string - description: ServiceName narrows to one reporting service. in: query name: serviceName required: false schema: type: string - description: Query narrows to issues whose text contains it. in: query name: query required: false schema: type: string - description: Sort orders the page, e.g. lastSeen, firstSeen, count. in: query name: sort required: false schema: type: string - description: Offset is how many issues to skip. Zero starts at the first. in: query name: offset required: false schema: format: int64 type: integer - description: Limit caps how many issues come back. Zero means the default. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yErrorIssuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller's org's grouped error issues (by fingerprint) with status, level, counts and first/last-seen. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/errortracking/issues/{id}: get: description: 'Returns one grouped issue with its latest occurrence sample. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_errortracking_issues_by_id parameters: - description: ID is the issue id. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yErrorGettableIssueOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one grouped issue with its latest occurrence sample. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: "Changes an issue's lifecycle \u2014 resolve, ignore, reopen or\nassign \u2014 and returns the updated\ \ issue. Fields left unset are left unchanged.\n\nCallers need the editor role; the runtime's own gate enforces it." operationId: post_o11y_errortracking_issues_by_id parameters: - description: ID is the issue id. in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yErrorUpdateIssueIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yErrorIssueOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Changes an issue's lifecycle \u2014 resolve, ignore, reopen or assign \u2014 and returns the updated issue." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/event: post: description: "Records one product-analytics event for the signed-in user \u2014 a track\nevent with a name and free-form\ \ attributes.\n\nCallers need the viewer role; the runtime's own gate enforces it." operationId: post_o11y_event requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yEventIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMessage' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Records one product-analytics event for the signed-in user \u2014 a track event with a name and free-form\ \ attributes." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/explorer/views: get: description: 'Lists the caller''s org''s saved explorer views, optionally narrowed to one source page, name or category. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_explorer_views parameters: - description: SourcePage narrows the views to one source page, e.g. traces, logs. in: query name: sourcePage required: false schema: type: string - description: Name narrows the views to one name. in: query name: name required: false schema: type: string - description: Category narrows the views to one category. in: query name: category required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySavedViewListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller's org's saved explorer views, optionally narrowed to one source page, name or category. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Saves a new explorer view for the caller''s org and returns its id. Callers need the editor role; the runtime''s own gate enforces it.' operationId: post_o11y_explorer_views requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.SavedView' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySavedViewCreateOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Saves a new explorer view for the caller's org and returns its id. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/explorer/views/{viewId}: delete: description: 'Deletes one saved explorer view by id. Callers need the editor role; the runtime''s own gate enforces it.' operationId: delete_o11y_explorer_views_by_viewid parameters: - description: ViewID is the view's id. in: path name: viewId required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySavedViewDeleteOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes one saved explorer view by id. tags: - o11y x-app: o11y x-public: true x-tool: true get: description: 'Returns one saved explorer view by id. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_explorer_views_by_viewid parameters: - description: ViewID is the view's id. in: path name: viewId required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySavedViewOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one saved explorer view by id. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: 'Replaces one saved explorer view by id with the given view and echoes it back. Callers need the editor role; the runtime''s own gate enforces it.' operationId: put_o11y_explorer_views_by_viewid parameters: - description: ViewID is the id of the view to replace, taken from the URL. in: path name: viewId required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySavedViewUpdateIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySavedViewOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Replaces one saved explorer view by id with the given view and echoes it back. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/export_raw_data: post: description: "Runs a query and returns its rows as a downloadable CSV or JSONL attachment, chunked, with a trailer that\ \ says whether the export completed \u2014 so a truncated download is detectable rather than silently short.\n\nThe\ \ answer is a file, not a value, which is why it is not a typed operation: the body is neither JSON nor bounded. Use\ \ the query operations when you want rows in a response.\n\nA validated, org-scoped principal is required and the\ \ export carries that principal's own tenant only." operationId: post_o11y_export_raw_data summary: Export raw telemetry rows as a file tags: - o11y x-app: o11y x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/o11y/features: get: description: 'Returns the supported feature flags and their resolved values for the caller''s org. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_features responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFeaturesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the supported feature flags and their resolved values for the caller's org. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/fields/keys: get: description: "Returns the telemetry field keys matching the selector \u2014 the\nsignal's fields grouped by name, and\ \ whether the catalog is complete.\n\nCallers need the viewer role; the runtime's own gate enforces it." operationId: get_o11y_fields_keys parameters: - description: "Signal is the telemetry to read the fields of \u2014 traces, logs or metrics." in: query name: signal required: false schema: type: string - description: Source narrows the fields to one source within the signal. in: query name: source required: false schema: type: string - description: Limit caps how many keys come back. in: query name: limit required: false schema: format: int64 type: integer - description: 'StartUnixMilli is the window start as a unix millisecond epoch. Zero reads as unset.' in: query name: startUnixMilli required: false schema: format: int64 type: integer - description: 'EndUnixMilli is the window end as a unix millisecond epoch. Zero reads as unset.' in: query name: endUnixMilli required: false schema: format: int64 type: integer - description: "FieldContext narrows the keys to one context \u2014 resource, scope, attribute,\nspan, log or metric." in: query name: fieldContext required: false schema: type: string - description: FieldDataType narrows the keys to one data type. in: query name: fieldDataType required: false schema: type: string - description: MetricName narrows the keys to those on one metric. in: query name: metricName required: false schema: type: string - description: MetricNamespace narrows the keys to one metric namespace. in: query name: metricNamespace required: false schema: type: string - description: SearchText narrows the keys to those containing it. in: query name: searchText required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFieldKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the telemetry field keys matching the selector \u2014 the signal's fields grouped by name, and whether\ \ the catalog is complete." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/fields/values: get: description: "Returns the values one telemetry field has taken \u2014 string, bool,\nnumber and related values \u2014\ \ and whether the value list is complete.\n\nCallers need the viewer role; the runtime's own gate enforces it." operationId: get_o11y_fields_values parameters: - description: "Signal is the telemetry to read the field of \u2014 traces, logs or metrics." in: query name: signal required: false schema: type: string - description: Source narrows the field to one source within the signal. in: query name: source required: false schema: type: string - description: Limit caps how many values come back. in: query name: limit required: false schema: format: int64 type: integer - description: 'StartUnixMilli is the window start as a unix millisecond epoch. Zero reads as unset.' in: query name: startUnixMilli required: false schema: format: int64 type: integer - description: 'EndUnixMilli is the window end as a unix millisecond epoch. Zero reads as unset.' in: query name: endUnixMilli required: false schema: format: int64 type: integer - description: FieldContext narrows the field to one context. in: query name: fieldContext required: false schema: type: string - description: FieldDataType narrows the field to one data type. in: query name: fieldDataType required: false schema: type: string - description: MetricName narrows the field to one metric. in: query name: metricName required: false schema: type: string - description: MetricNamespace narrows the field to one metric namespace. in: query name: metricNamespace required: false schema: type: string - description: SearchText narrows the values to those containing it. in: query name: searchText required: false schema: type: string - description: Name is the field whose values to read. in: query name: name required: false schema: type: string - description: 'ExistingQuery is the query the field appears in, so related values can be suggested for it.' in: query name: existingQuery required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFieldValuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the values one telemetry field has taken \u2014 string, bool, number and related values \u2014 and\ \ whether the value list is complete." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/filter_suggestions: get: description: 'Suggests attribute keys and example filter queries for the query builder, seeded by what the org''s own telemetry carries. Only the logs data source is supported today. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_filter_suggestions parameters: - description: 'DataSource is the signal suggestions are drawn from; only logs is supported today. Required.' in: query name: dataSource required: true schema: type: string - description: SearchText narrows attribute suggestions to keys containing it. in: query name: searchText required: false schema: type: string - description: 'ExistingFilter is the current filter set, JSON base64url-encoded, so example queries build on it rather than repeat it.' in: query name: existingFilter required: false schema: type: string - description: AttributesLimit caps how many attribute keys come back. in: query name: attributesLimit required: false schema: format: int64 type: integer - description: ExamplesLimit caps how many example queries come back. in: query name: examplesLimit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFilterSuggestionsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Suggests attribute keys and example filter queries for the query builder, seeded by what the org's own telemetry carries. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/gateway/ingestion_keys: get: description: 'Lists the workspace''s ingestion keys, paginated. Editor gate.' operationId: GetIngestionKeys parameters: - description: Page is the 1-based page number. in: query name: page required: false schema: format: int64 type: integer - description: PerPage is the page size. in: query name: per_page required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yIngestionKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the workspace's ingestion keys, paginated. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Mints an ingestion key for the workspace, answering with the created key. Editor gate.' operationId: CreateIngestionKey requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostableIngestionKey' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yCreatedIngestionKeyOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Mints an ingestion key for the workspace, answering with the created key. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/gateway/ingestion_keys/limits/{limitId}: delete: description: 'Removes an ingestion key limit, by limit id. Editor gate.' operationId: DeleteIngestionKeyLimit parameters: - in: path name: limitId required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes an ingestion key limit, by limit id. tags: - o11y x-app: o11y x-public: true x-tool: true patch: description: 'Changes an ingestion key limit, by limit id. Editor gate.' operationId: UpdateIngestionKeyLimit parameters: - in: path name: limitId required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yUpdateLimitIn' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes an ingestion key limit, by limit id. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/gateway/ingestion_keys/search: get: description: 'Lists the workspace''s ingestion keys whose name matches the search, paginated. Editor gate.' operationId: SearchIngestionKeys parameters: - description: Name is the substring to match ingestion-key names against. in: query name: name required: false schema: type: string - description: Page is the 1-based page number. in: query name: page required: false schema: format: int64 type: integer - description: PerPage is the page size. in: query name: per_page required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yIngestionKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the workspace's ingestion keys whose name matches the search, paginated. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/gateway/ingestion_keys/{keyId}: delete: description: Removes an ingestion key, by id. Editor gate. operationId: DeleteIngestionKey parameters: - in: path name: keyId required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes an ingestion key, by id. tags: - o11y x-app: o11y x-public: true x-tool: true patch: description: Changes an ingestion key, by id. Editor gate. operationId: UpdateIngestionKey parameters: - in: path name: keyId required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yUpdateIngestionKeyIn' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes an ingestion key, by id. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/gateway/ingestion_keys/{keyId}/limits: post: description: 'Sets a signal limit on an ingestion key, by key id, answering with the created limit. Editor gate.' operationId: CreateIngestionKeyLimit parameters: - in: path name: keyId required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yCreateLimitIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yCreatedLimitOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Sets a signal limit on an ingestion key, by key id, answering with the created limit. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/global/config: get: description: "Returns the deployment's global configuration: its public\nendpoints and which identity providers are\ \ enabled. Open by design \u2014 the\nsign-in page reads it before anyone is signed in.\n\nOpen by design; the runtime's\ \ own gate is OpenAccess." operationId: get_o11y_global_config responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yGlobalConfigOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the deployment''s global configuration: its public endpoints and which identity providers are enabled.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/health: get: description: 'Reports service health. With live set, the datastore connection is checked too and an unhealthy store refuses with 503. Open by design; the runtime''s own gate is OpenAccess.' operationId: get_o11y_health parameters: - description: 'Live also checks the datastore connection; an unreachable store refuses with 503.' in: query name: live required: false schema: type: boolean responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yHealthOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports service health. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/healthz: get: description: "Reports whether every service in the runtime's registry is healthy, and names them grouped by state \u2014\ \ so a failure says WHICH component is down, not merely that something is. An unhealthy registry answers 503, not\ \ a 200 with a false flag inside, so a plain status check cannot read a sick runtime as well.\n\nUNAUTHENTICATED by\ \ design, like the other two probes: it carries no tenant data and is reached by k8s and by external checks that hold\ \ no principal." operationId: get_o11y_healthz summary: Health of the observability runtime's services tags: - o11y x-app: o11y x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/o11y/hosts/attribute_keys: get: description: "Lists the metric attribute keys hosts report, for building\nhost filters \u2014 each with its data type\ \ and whether it is a materialized\ncolumn." operationId: get_o11y_hosts_attribute_keys parameters: - description: "DataSource is the telemetry the keys come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the keys will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the keys must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: SearchText narrows the keys to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the keys to one kind \u2014 tag or resource. Empty means all;\nan invalid value reads\ \ as empty." in: query name: tagType required: false schema: type: string - description: Limit caps how many keys come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the metric attribute keys hosts report, for building host filters \u2014 each with its data type and\ \ whether it is a materialized column." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/hosts/attribute_values: get: description: "Lists the values one host attribute key has taken, for\nbuilding host filters \u2014 string, number and\ \ bool values in their own lists." operationId: get_o11y_hosts_attribute_values parameters: - description: "DataSource is the telemetry the values come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the values will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the values must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: AttributeKey is the key whose values to list. in: query name: attributeKey required: false schema: type: string - description: "FilterAttributeKeyDataType is the key's data type \u2014 string, int64,\nfloat64 or bool. Empty means\ \ unspecified." in: query name: filterAttributeKeyDataType required: false schema: type: string - description: SearchText narrows the values to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the search to one kind of key \u2014 tag or resource." in: query name: tagType required: false schema: type: string - description: Limit caps how many values come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeValuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the values one host attribute key has taken, for building host filters \u2014 string, number and bool\ \ values in their own lists." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/hosts/list: post: description: 'Lists monitored hosts over a time range, each with its CPU, memory, I/O wait and 15-minute load, whether it is actively reporting, its OS and its attributes; filterable, groupable and paginated. The answer also says whether any host metrics were received at all and which clusters and nodes sent them, so an empty page is distinguishable from a fleet that never reported.' operationId: post_o11y_hosts_list requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.HostListRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yHostListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists monitored hosts over a time range, each with its CPU, memory, I/O wait and 15-minute load, whether it is actively reporting, its OS and its attributes; filterable, groupable and paginated. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/infra_monitoring/checks: get: description: "Reports whether the metrics and attributes an infra-monitoring\nsection needs are being received \u2014\ \ for each collector receiver or processor\ninvolved, what is present and what is missing, with a user-facing message\n\ and a docs link per missing piece. Ready is true only when nothing is\nmissing." operationId: get_o11y_infra_monitoring_checks parameters: - description: "Type is the section to check \u2014 hosts, processes, pods, nodes,\ndeployments, daemonsets, statefulsets,\ \ jobs, namespaces, clusters or\nvolumes. Required." in: query name: type required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraChecksOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Reports whether the metrics and attributes an infra-monitoring section needs are being received \u2014 for\ \ each collector receiver or processor involved, what is present and what is missing, with a user-facing message and\ \ a docs link per missing piece." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/infra_monitoring/clusters: post: description: 'Lists Kubernetes clusters with CPU and memory usage against allocatable capacity summed over their nodes, plus per-group node readiness and pod phase counts. Rows answer as ''list'' under the default k8s.cluster.name grouping or ''grouped_list'' under a custom groupBy; a metric with no data in the window answers -1. Filterable by expression, orderable by usage or allocatable, paginated by offset and limit.' operationId: post_o11y_infra_monitoring_clusters requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostableClusters' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraClustersOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes clusters with CPU and memory usage against allocatable capacity summed over their nodes, plus per-group node readiness and pod phase counts. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/infra_monitoring/daemonsets: post: description: 'Lists Kubernetes daemonsets with the CPU and memory their pods used against request and limit, the latest desired and current scheduled NODE counts (node counts, not pod counts), and per-group pod phase counts. Rows answer as ''list'' under the default k8s.daemonset.name grouping or ''grouped_list'' under a custom groupBy; a metric with no data in the window answers -1. Filterable by expression, orderable by the pod metrics or the node counts, paginated by offset and limit.' operationId: post_o11y_infra_monitoring_daemonsets requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostableDaemonSets' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraDaemonSetsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes daemonsets with the CPU and memory their pods used against request and limit, the latest desired and current scheduled NODE counts (node counts, not pod counts), and per-group pod phase counts. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/infra_monitoring/deployments: post: description: 'Lists Kubernetes deployments with the CPU and memory their pods used against request and limit, the latest desired and available replica counts, and per-group pod phase counts. Rows answer as ''list'' under the default k8s.deployment.name grouping or ''grouped_list'' under a custom groupBy; a metric with no data in the window answers -1. Filterable by expression, orderable by the pod metrics or the replica counts, paginated by offset and limit.' operationId: post_o11y_infra_monitoring_deployments requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostableDeployments' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraDeploymentsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes deployments with the CPU and memory their pods used against request and limit, the latest desired and available replica counts, and per-group pod phase counts. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/infra_monitoring/hosts: post: description: "Lists hosts with key infrastructure metrics \u2014 CPU, memory, I/O\nwait and disk usage percentages and\ \ 15-minute load \u2014 plus an\nactive/inactive status from whether the host reported in the last ten\nminutes. Rows\ \ answer as 'list' under the default host.name grouping or\n'grouped_list' under a custom groupBy; a metric with no\ \ data in the window\nanswers -1. Filterable by expression and by status, orderable by any of the\nfive metrics, paginated\ \ by offset and limit." operationId: post_o11y_infra_monitoring_hosts requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostableHosts' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraHostsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists hosts with key infrastructure metrics \u2014 CPU, memory, I/O wait and disk usage percentages and 15-minute\ \ load \u2014 plus an active/inactive status from whether the host reported in the last ten minutes." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/infra_monitoring/jobs: post: description: "Lists Kubernetes jobs with the CPU and memory their pods used\nagainst request and limit, the latest desired-successful,\ \ active, failed and\nsuccessful pod counters, and per-group pod phase counts \u2014 the phase counts\nare current\ \ state while the counters are cumulative over the job's life.\nRows answer as 'list' under the default k8s.job.name\ \ grouping or\n'grouped_list' under a custom groupBy; a metric with no data in the window\nanswers -1. Filterable\ \ by expression, orderable by the pod metrics or the\njob counters, paginated by offset and limit." operationId: post_o11y_infra_monitoring_jobs requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostableJobs' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraJobsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists Kubernetes jobs with the CPU and memory their pods used against request and limit, the latest desired-successful,\ \ active, failed and successful pod counters, and per-group pod phase counts \u2014 the phase counts are current state\ \ while the counters are cumulative over the job's life." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/infra_monitoring/namespaces: post: description: 'Lists Kubernetes namespaces with the CPU and memory their pods used and per-group pod phase counts. Rows answer as ''list'' under the default k8s.namespace.name grouping or ''grouped_list'' under a custom groupBy, aggregating pods either way; a metric with no data in the window answers -1. Filterable by expression, orderable by cpu or memory, paginated by offset and limit.' operationId: post_o11y_infra_monitoring_namespaces requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostableNamespaces' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraNamespacesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes namespaces with the CPU and memory their pods used and per-group pod phase counts. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/infra_monitoring/nodes: post: description: 'Lists Kubernetes nodes with CPU and memory usage against allocatable capacity, per-group readiness counts and per-group phase counts for the pods scheduled on them. Rows answer as ''list'' under the default k8s.node.name grouping (each row one node with its readiness condition) or ''grouped_list'' under a custom groupBy; a metric with no data in the window answers -1. Filterable by expression, orderable by usage or allocatable, paginated by offset and limit.' operationId: post_o11y_infra_monitoring_nodes requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostableNodes' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraNodesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes nodes with CPU and memory usage against allocatable capacity, per-group readiness counts and per-group phase counts for the pods scheduled on them. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/infra_monitoring/pods: post: description: 'Lists Kubernetes pods with CPU and memory usage against request and limit, the pod''s phase and its age, plus its namespace, node, owning workload and cluster attributes. Rows answer as ''list'' under the default k8s.pod.uid grouping (each row one pod) or ''grouped_list'' under a custom groupBy (each row aggregating its pods with per-phase counts); a metric with no data in the window answers -1. Filterable by expression, orderable by the six pod metrics, paginated by offset and limit.' operationId: post_o11y_infra_monitoring_pods requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostablePods' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraPodsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes pods with CPU and memory usage against request and limit, the pod's phase and its age, plus its namespace, node, owning workload and cluster attributes. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/infra_monitoring/pvcs: post: description: 'Lists Kubernetes persistent volume claims with available, capacity and used bytes and inode counts, plus the claim''s pod, namespace, node, statefulset and cluster attributes. Rows answer as ''list'' under the default k8s.persistentvolumeclaim.name grouping or ''grouped_list'' under a custom groupBy; a metric with no data in the window answers -1. Filterable by expression, orderable by the six volume metrics, paginated by offset and limit.' operationId: post_o11y_infra_monitoring_pvcs requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostableVolumes' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraVolumesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes persistent volume claims with available, capacity and used bytes and inode counts, plus the claim's pod, namespace, node, statefulset and cluster attributes. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/infra_monitoring/statefulsets: post: description: 'Lists Kubernetes statefulsets with the CPU and memory their pods used against request and limit, the latest desired and current replica counts, and per-group pod phase counts. Rows answer as ''list'' under the default k8s.statefulset.name grouping or ''grouped_list'' under a custom groupBy; a metric with no data in the window answers -1. Filterable by expression, orderable by the pod metrics or the replica counts, paginated by offset and limit.' operationId: post_o11y_infra_monitoring_statefulsets requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostableStatefulSets' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraStatefulSetsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes statefulsets with the CPU and memory their pods used against request and limit, the latest desired and current replica counts, and per-group pod phase counts. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/infra_onboarding/k8s/status: get: description: 'Reports how far Kubernetes infra onboarding has progressed: which metric families have arrived and, per pod, which required metadata labels are present. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_infra_onboarding_k8s_status responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yOnboardingOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Reports how far Kubernetes infra onboarding has progressed: which metric families have arrived and, per pod, which required metadata labels are present.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/integrations: get: description: 'Lists the available integrations and whether each is installed in the caller''s org, optionally narrowed to installed or not-installed. Viewer gate.' operationId: ListIntegrations parameters: - description: 'IsInstalled, when "true" or "false", keeps only integrations in that installed state; empty lists them all.' in: query name: is_installed required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yIntegrationsListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the available integrations and whether each is installed in the caller's org, optionally narrowed to installed or not-installed. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/integrations/install: post: description: 'Installs an integration into the caller''s org from its id and configuration, answering with the installed catalog item. Viewer gate.' operationId: InstallIntegration requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.InstallIntegrationRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInstallOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Installs an integration into the caller's org from its id and configuration, answering with the installed catalog item. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/integrations/uninstall: post: description: 'Removes an integration from the caller''s org by id. Viewer gate.' operationId: UninstallIntegration requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.UninstallIntegrationRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yIntegrationAck' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes an integration from the caller's org by id. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/integrations/{integrationId}: get: description: "Returns one integration's full detail \u2014 its overview,\nconfiguration steps, collected data and assets\ \ \u2014 together with its\ninstallation record when the org has installed it. Viewer gate." operationId: GetIntegration parameters: - in: path name: integrationId required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yIntegrationOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one integration's full detail \u2014 its overview, configuration steps, collected data and assets\ \ \u2014 together with its installation record when the org has installed it." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/integrations/{integrationId}/connection_status: get: description: 'Reports whether the integration''s logs and metrics have been received over the lookback window, so the console can show a live connection state. An integration that is not installed answers with an empty status rather than an error. Viewer gate.' operationId: GetIntegrationConnectionStatus parameters: - in: path name: integrationId required: true schema: type: string - description: 'LookbackSeconds is how far back to look for received telemetry, in seconds.' in: query name: lookback_seconds required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yConnectionStatusOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports whether the integration's logs and metrics have been received over the lookback window, so the console can show a live connection state. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/jobs/attribute_keys: get: description: 'Lists the metric attribute keys Kubernetes jobs report, for building job filters.' operationId: get_o11y_jobs_attribute_keys parameters: - description: "DataSource is the telemetry the keys come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the keys will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the keys must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: SearchText narrows the keys to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the keys to one kind \u2014 tag or resource. Empty means all;\nan invalid value reads\ \ as empty." in: query name: tagType required: false schema: type: string - description: Limit caps how many keys come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the metric attribute keys Kubernetes jobs report, for building job filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/jobs/attribute_values: get: description: 'Lists the values one job attribute key has taken, for building job filters.' operationId: get_o11y_jobs_attribute_values parameters: - description: "DataSource is the telemetry the values come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the values will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the values must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: AttributeKey is the key whose values to list. in: query name: attributeKey required: false schema: type: string - description: "FilterAttributeKeyDataType is the key's data type \u2014 string, int64,\nfloat64 or bool. Empty means\ \ unspecified." in: query name: filterAttributeKeyDataType required: false schema: type: string - description: SearchText narrows the values to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the search to one kind of key \u2014 tag or resource." in: query name: tagType required: false schema: type: string - description: Limit caps how many values come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeValuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the values one job attribute key has taken, for building job filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/jobs/list: post: description: 'Lists Kubernetes jobs over a time range, each with the CPU and memory its pods used against request and limit, desired-successful, active, failed and successful pod counts, restarts and attributes; filterable, groupable and paginated.' operationId: post_o11y_jobs_list requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.JobListRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yJobListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes jobs over a time range, each with the CPU and memory its pods used against request and limit, desired-successful, active, failed and successful pod counts, restarts and attributes; filterable, groupable and paginated. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/licenses: get: description: 'Lists the org''s licenses. This build has no enterprise edition, so the list is intentionally empty. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_licenses responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLicensesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the org's licenses. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/licenses/active: get: description: 'Activates the enterprise license. This build has no enterprise edition, so the licensing provider refuses it as unsupported. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_licenses_active responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLicenseActiveOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Activates the enterprise license. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/listErrors: post: description: "Lists the grouped exceptions in the query window \u2014 each an\nexception type with its message, count,\ \ service and first/last-seen \u2014 for the\ncaller's org.\n\nCallers need the viewer role; the runtime's own gate\ \ enforces it." operationId: post_o11y_listerrors requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yErrorsListIn' required: true responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/o11y.O11yListError' type: array description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the grouped exceptions in the query window \u2014 each an exception type with its message, count, service\ \ and first/last-seen \u2014 for the caller's org." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/livez: get: description: 'Answers 200 unconditionally while the process is running, and asserts NOTHING about the telemetry stores behind it. That is what makes it a liveness probe: a container that answers this is worth leaving alive, and restarting on a store outage would only remove the thing reporting the outage. UNAUTHENTICATED by design, and one of exactly three /v1/o11y paths that are. It carries no tenant data, and gating it would break the k8s probes and the external health checks without protecting anything. Use the health probe, not this one, to ask whether the runtime can actually serve.' operationId: get_o11y_livez summary: Liveness of the observability process tags: - o11y x-app: o11y x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/o11y/llm/annotation: get: description: 'Lists human annotations on traces and observations, optionally scoped to one review queue. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: ListLLMAnnotations parameters: - description: TraceID narrows to annotations on one trace. in: query name: traceId required: false schema: type: string - description: Queue narrows to one review queue. in: query name: queue required: false schema: type: string - description: Status narrows to one review status, e.g. PENDING. in: query name: status required: false schema: type: string - description: Offset is how many rows to skip, for paging. in: query name: offset required: false schema: format: int64 type: integer - description: Limit caps how many rows come back. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLLMAnnotationsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists human annotations on traces and observations, optionally scoped to one review queue. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Adds a human annotation to a trace or observation, optionally in a review queue. Callers need the editor role; the runtime''s own gate enforces it, and it validates the payload and stamps the annotation''s author and org.' operationId: CreateLLMAnnotation requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLLMIngestAnnotation' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLLMAnnotationOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Adds a human annotation to a trace or observation, optionally in a review queue. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/llm/observations: get: description: "Lists gen_ai spans as LLM observations \u2014 each an LLM call with\nits model, token counts, cost and\ \ latency projected from gen_ai.* attributes,\nnewest first, over the query window.\n\nCallers need the viewer role;\ \ the runtime's own gate enforces it, and scopes\nthe read to the caller's validated tenant." operationId: ListLLMObservations parameters: - description: 'Start is the start of the window as a unix-millisecond epoch. Zero means 24h before the end.' in: query name: start required: false schema: format: int64 type: integer - description: End is the end of the window as a unix-millisecond epoch. Zero means now. in: query name: end required: false schema: format: int64 type: integer - description: TraceID narrows the view to one trace. in: query name: traceId required: false schema: type: string - description: SessionID narrows the view to one conversation. in: query name: sessionId required: false schema: type: string - description: UserID narrows the view to one end user. in: query name: userId required: false schema: type: string - description: Name narrows the view to observations of one name. in: query name: name required: false schema: type: string - description: Model narrows the view to one model. in: query name: model required: false schema: type: string - description: Offset is how many rows to skip, for paging. in: query name: offset required: false schema: format: int64 type: integer - description: Limit caps how many rows come back. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLLMObservationsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists gen_ai spans as LLM observations \u2014 each an LLM call with its model, token counts, cost and latency\ \ projected from gen_ai.* attributes, newest first, over the query window." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/llm/score/{id}: delete: description: 'Hard-deletes a score by id. Callers need the editor role; the runtime''s own gate enforces it.' operationId: DeleteLLMScore parameters: - in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Hard-deletes a score by id. tags: - o11y x-app: o11y x-public: true x-tool: true get: description: 'Returns a single score by id. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: GetLLMScore parameters: - in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLLMScoreOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns a single score by id. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/llm/scores: get: description: 'Lists eval scores and human-feedback signals attached to traces and observations, newest first. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: ListLLMScores parameters: - description: TraceID narrows to scores on one trace. in: query name: traceId required: false schema: type: string - description: ObservationID narrows to scores on one observation. in: query name: observationId required: false schema: type: string - description: Name narrows to scores of one name. in: query name: name required: false schema: type: string - description: Source narrows to scores from one source, e.g. API, EVAL. in: query name: source required: false schema: type: string - description: Offset is how many rows to skip, for paging. in: query name: offset required: false schema: format: int64 type: integer - description: Limit caps how many rows come back. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLLMScoresOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists eval scores and human-feedback signals attached to traces and observations, newest first. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Attaches an eval score or human-feedback signal to a trace or a single observation. Callers need the editor role; the runtime''s own gate enforces it, and it validates the payload and stamps the score''s author and org.' operationId: CreateLLMScore requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLLMIngestScore' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLLMScoreOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Attaches an eval score or human-feedback signal to a trace or a single observation. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/llm/sessions: get: description: "Lists conversations \u2014 gen_ai spans grouped by session.id, with\ntheir trace and observation counts,\ \ tokens and cost.\n\nCallers need the viewer role; the runtime's own gate enforces it." operationId: ListLLMSessions parameters: - description: 'Start is the start of the window as a unix-millisecond epoch. Zero means 24h before the end.' in: query name: start required: false schema: format: int64 type: integer - description: End is the end of the window as a unix-millisecond epoch. Zero means now. in: query name: end required: false schema: format: int64 type: integer - description: TraceID narrows the view to one trace. in: query name: traceId required: false schema: type: string - description: SessionID narrows the view to one conversation. in: query name: sessionId required: false schema: type: string - description: UserID narrows the view to one end user. in: query name: userId required: false schema: type: string - description: Name narrows the view to observations of one name. in: query name: name required: false schema: type: string - description: Model narrows the view to one model. in: query name: model required: false schema: type: string - description: Offset is how many rows to skip, for paging. in: query name: offset required: false schema: format: int64 type: integer - description: Limit caps how many rows come back. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLLMSessionsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists conversations \u2014 gen_ai spans grouped by session.id, with their trace and observation counts, tokens\ \ and cost." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/llm/traces: get: description: "Lists LLM traces \u2014 gen_ai spans grouped by trace_id, with cost,\ntokens and latency rolled up across\ \ each trace.\n\nCallers need the viewer role; the runtime's own gate enforces it." operationId: ListLLMTraces parameters: - description: 'Start is the start of the window as a unix-millisecond epoch. Zero means 24h before the end.' in: query name: start required: false schema: format: int64 type: integer - description: End is the end of the window as a unix-millisecond epoch. Zero means now. in: query name: end required: false schema: format: int64 type: integer - description: TraceID narrows the view to one trace. in: query name: traceId required: false schema: type: string - description: SessionID narrows the view to one conversation. in: query name: sessionId required: false schema: type: string - description: UserID narrows the view to one end user. in: query name: userId required: false schema: type: string - description: Name narrows the view to observations of one name. in: query name: name required: false schema: type: string - description: Model narrows the view to one model. in: query name: model required: false schema: type: string - description: Offset is how many rows to skip, for paging. in: query name: offset required: false schema: format: int64 type: integer - description: Limit caps how many rows come back. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLLMTracesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists LLM traces \u2014 gen_ai spans grouped by trace_id, with cost, tokens and latency rolled up across each\ \ trace." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/llm/users: get: description: "Lists end users \u2014 gen_ai spans grouped by user.id, with their\nsession, trace and observation counts,\ \ tokens and cost.\n\nCallers need the viewer role; the runtime's own gate enforces it." operationId: ListLLMUsers parameters: - description: 'Start is the start of the window as a unix-millisecond epoch. Zero means 24h before the end.' in: query name: start required: false schema: format: int64 type: integer - description: End is the end of the window as a unix-millisecond epoch. Zero means now. in: query name: end required: false schema: format: int64 type: integer - description: TraceID narrows the view to one trace. in: query name: traceId required: false schema: type: string - description: SessionID narrows the view to one conversation. in: query name: sessionId required: false schema: type: string - description: UserID narrows the view to one end user. in: query name: userId required: false schema: type: string - description: Name narrows the view to observations of one name. in: query name: name required: false schema: type: string - description: Model narrows the view to one model. in: query name: model required: false schema: type: string - description: Offset is how many rows to skip, for paging. in: query name: offset required: false schema: format: int64 type: integer - description: Limit caps how many rows come back. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLLMUsersOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists end users \u2014 gen_ai spans grouped by user.id, with their session, trace and observation counts,\ \ tokens and cost." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/llm_pricing_rules: get: description: 'Returns the LLM pricing rules for the caller''s org, with pagination and an optional search and override filter. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: ListLLMPricingRules parameters: - description: Search matches rules by model or provider. in: query name: q required: false schema: type: string - description: 'IsOverride, when "true" or "false", narrows to user-pinned rules or to synced ones; empty returns both. It is a string because a query param is a string on the wire, and the runtime reads absent as "no filter".' in: query name: isOverride required: false schema: type: string - description: Offset is how many rows to skip, for paging. in: query name: offset required: false schema: format: int64 type: integer - description: Limit caps how many rows come back. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLLMPricingRulesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the LLM pricing rules for the caller's org, with pagination and an optional search and override filter. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: "Writes the pricing-rule batch \u2014 the single write\nendpoint used by both the user and the Zeus sync\ \ job. Per-rule match is by id,\nthen sourceId, then insert; an override row is fully preserved when the\nrequest\ \ omits isOverride, only its synced_at stamped.\n\nCallers need the admin role; the runtime's own gate enforces it." operationId: CreateOrUpdateLLMPricingRules requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLLMUpdatablePricingRules' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Writes the pricing-rule batch \u2014 the single write endpoint used by both the user and the Zeus sync job." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/llm_pricing_rules/{id}: delete: description: 'Hard-deletes a pricing rule by id. If the rule was auto-synced, the next sync cycle recreates it. Callers need the admin role; the runtime''s own gate enforces it.' operationId: DeleteLLMPricingRule parameters: - in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Hard-deletes a pricing rule by id. tags: - o11y x-app: o11y x-public: true x-tool: true get: description: 'Returns a single LLM pricing rule by id. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: GetLLMPricingRule parameters: - in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLLMPricingRuleOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns a single LLM pricing rule by id. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/login: get: description: "Where a failed sign-in callback lands. The module builds that redirect with a path and no host, so it\ \ can only be same-origin, and its assumption is that the console lives beside the API. Here the console is a separate\ \ host, so this answers as the API it belongs to rather than redirecting onward \u2014 a hostname baked into a shared\ \ surface is how one brand's identity ends up in front of another brand's customer.\n\n401 with the provider's own\ \ reason, carried from the query the module already populated. Unauthenticated by necessity: it exists precisely for\ \ the case where no principal was established." operationId: get_o11y_login summary: Report why an observability sign-in did not complete tags: - o11y x-app: o11y x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/o11y/logs: get: description: "Returns the most recent log records in the query window, newest\nfirst \u2014 each record an open object\ \ carrying its nanosecond timestamp and\nwhatever fields the record was ingested with.\n\nCallers need the viewer\ \ role; the runtime's own gate enforces it." operationId: get_o11y_logs parameters: - description: Limit caps how many records come back. Zero means the default of 100. in: query name: limit required: false schema: format: int64 type: integer - description: 'TimestampStart is the start of the window as a nanosecond epoch. Zero means fifteen minutes before the end.' in: query name: timestampStart required: false schema: format: int64 type: integer - description: 'TimestampEnd is the end of the window as a nanosecond epoch. Zero means now.' in: query name: timestampEnd required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLogRecordsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the most recent log records in the query window, newest first \u2014 each record an open object carrying\ \ its nanosecond timestamp and whatever fields the record was ingested with." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/logs/aggregate: get: description: 'Returns the logs aggregate buckets for the query window. The runtime currently answers the empty set; the shape is the contract. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_logs_aggregate responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLogAggregateOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the logs aggregate buckets for the query window. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/logs/fields: get: description: 'Returns the log field catalog: the fields already selected as indexed columns, and the interesting ones seen in the data that could be. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_logs_fields responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFieldCatalogOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the log field catalog: the fields already selected as indexed columns, and the interesting ones seen in the data that could be.' tags: - o11y x-app: o11y x-public: true x-tool: true post: description: "Changes how one log field is stored \u2014 selects or deselects it\nas a materialized column and tunes\ \ its index \u2014 and echoes the setting back.\n\nCallers need the editor role; the runtime's own gate enforces it." operationId: post_o11y_logs_fields requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFieldSetting' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFieldSetting' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Changes how one log field is stored \u2014 selects or deselects it as a materialized column and tunes its\ \ index \u2014 and echoes the setting back." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/logs/livetail: get: description: 'Streams matching log records continuously instead of answering once, so a console tail shows lines as they land rather than at the end of a window. It is a STREAM, which is why it is not a typed operation: there is no single complete value to name, and a generated client that waited for one would hang on the first tail. Read the bounded window with the log read instead when you want an answer rather than a feed. A validated, org-scoped principal is required and the feed carries that principal''s own tenant only.' operationId: get_o11y_logs_livetail summary: Follow log records as they arrive tags: - o11y x-app: o11y x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/o11y/logs/pipelines: post: description: 'Saves the given log parsing pipelines as the new config version for the caller''s org and starts deploying it. The set REPLACES the current one: a pipeline left out of the request is dropped from the new version, and an empty set drops them all. Callers need the editor role; the runtime''s own gate enforces it.' operationId: post_o11y_logs_pipelines requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLogPipelineCreateIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLogPipelinesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Saves the given log parsing pipelines as the new config version for the caller's org and starts deploying it. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/logs/pipelines/preview: post: description: 'Runs the given log parsing pipelines over the given sample records without saving anything, and returns the transformed records plus whatever the collector logged while simulating them. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: post_o11y_logs_pipelines_preview requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLogPipelinePreviewIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLogPipelinePreviewOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Runs the given log parsing pipelines over the given sample records without saving anything, and returns the transformed records plus whatever the collector logged while simulating them. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/logs/pipelines/{version}: get: description: "Returns the caller's org's log parsing pipelines at one config\nversion \u2014 \"latest\" for the newest\ \ \u2014 along with that version's deployment\nrecord and the recent version history.\n\nCallers need the viewer role;\ \ the runtime's own gate enforces it." operationId: get_o11y_logs_pipelines_by_version parameters: - description: "Version is the config version to read \u2014 a positive number, or \"latest\"." in: path name: version required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLogPipelinesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the caller's org's log parsing pipelines at one config version \u2014 \"latest\" for the newest \u2014\ \ along with that version's deployment record and the recent version history." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/logs/promote_paths: get: description: 'Lists the log body paths already promoted or indexed, with the indexes each carries. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_logs_promote_paths responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLogPromotedOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the log body paths already promoted or indexed, with the indexes each carries. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Promotes and indexes log body paths: each named path is lifted out of the JSON body into its own column, with the indexes the caller asked for. Paths must start with "body.". Callers need the editor role; the runtime''s own gate enforces it.' operationId: post_o11y_logs_promote_paths requestBody: content: application/json: schema: items: $ref: '#/components/schemas/o11y.O11yLogPromotePath' type: array required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLogPromoteOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Promotes and indexes log body paths: each named path is lifted out of the JSON body into its own column, with the indexes the caller asked for.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/messaging-queues/kafka/consumer-lag/consumer-details: post: description: 'Returns the consumer side of a consumer-lag view: the consumer groups reading the topic/partition named in variables, with their throughput and latency over the window.' operationId: post_o11y_messaging-queues_kafka_consumer-lag_consumer-details requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueryRangeOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the consumer side of a consumer-lag view: the consumer groups reading the topic/partition named in variables, with their throughput and latency over the window.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/messaging-queues/kafka/consumer-lag/network-latency: post: description: 'Returns consumer network latency correlated per client: a throughput pass over the window finds the consumer clients, then their fetch latency joins in as a latency column per client/instance/service.' operationId: post_o11y_messaging-queues_kafka_consumer-lag_network-latency requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueryRangeOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns consumer network latency correlated per client: a throughput pass over the window finds the consumer clients, then their fetch latency joins in as a latency column per client/instance/service.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/messaging-queues/kafka/consumer-lag/producer-details: post: description: 'Returns the producer side of a consumer-lag view: the producers writing to the topic/partition named in variables, with their throughput and latency over the window.' operationId: post_o11y_messaging-queues_kafka_consumer-lag_producer-details requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueryRangeOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the producer side of a consumer-lag view: the producers writing to the topic/partition named in variables, with their throughput and latency over the window.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/messaging-queues/kafka/onboarding/consumers: post: description: 'Checks whether the spans the Kafka consumer views need are arriving, row for row like producersOnboarding.' operationId: post_o11y_messaging-queues_kafka_onboarding_consumers requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueChecksOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Checks whether the spans the Kafka consumer views need are arriving, row for row like producersOnboarding. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/messaging-queues/kafka/onboarding/kafka: post: description: "Checks whether Kafka's own metrics \u2014 consumer lag and\npartition telemetry \u2014 are arriving, so\ \ the lag views can be lit up." operationId: post_o11y_messaging-queues_kafka_onboarding_kafka requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueChecksOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Checks whether Kafka's own metrics \u2014 consumer lag and partition telemetry \u2014 are arriving, so the\ \ lag views can be lit up." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/messaging-queues/kafka/onboarding/producers: post: description: "Checks whether the spans the Kafka producer views need\nare arriving \u2014 one row per required span\ \ attribute, with a pass/fail status\nand, on failure, what is missing from the instrumentation." operationId: post_o11y_messaging-queues_kafka_onboarding_producers requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueChecksOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Checks whether the spans the Kafka producer views need are arriving \u2014 one row per required span attribute,\ \ with a pass/fail status and, on failure, what is missing from the instrumentation." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/messaging-queues/kafka/partition-latency/consumer: post: description: 'Returns the consumer-group latency detail for the topic and partition named in the request''s variables.' operationId: post_o11y_messaging-queues_kafka_partition-latency_consumer requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueryRangeOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the consumer-group latency detail for the topic and partition named in the request's variables. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/messaging-queues/kafka/partition-latency/overview: post: description: "Returns the per-partition latency overview for the window \u2014\neach topic/partition with its throughput\ \ and latency profile." operationId: post_o11y_messaging-queues_kafka_partition-latency_overview requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueryRangeOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the per-partition latency overview for the window \u2014 each topic/partition with its throughput\ \ and latency profile." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/messaging-queues/kafka/span/evaluation: post: description: "Correlates producer and consumer spans over the evaluation\nwindow (eval_time bounds the scan) and returns\ \ the pairings with their\nend-to-end delay \u2014 the check that messages produced are being consumed." operationId: post_o11y_messaging-queues_kafka_span_evaluation requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueryRangeOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Correlates producer and consumer spans over the evaluation window (eval_time bounds the scan) and returns\ \ the pairings with their end-to-end delay \u2014 the check that messages produced are being consumed." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/messaging-queues/kafka/topic-throughput/consumer: post: description: "Returns the consumer topic-throughput overview for the\nwindow \u2014 what each consumer group read, per\ \ topic." operationId: post_o11y_messaging-queues_kafka_topic-throughput_consumer requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueryRangeOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the consumer topic-throughput overview for the window \u2014 what each consumer group read, per topic." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/messaging-queues/kafka/topic-throughput/consumer-details: post: description: 'Breaks one consumer topic''s throughput down using the topic and service named in variables.' operationId: post_o11y_messaging-queues_kafka_topic-throughput_consumer-details requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueryRangeOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Breaks one consumer topic's throughput down using the topic and service named in variables. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/messaging-queues/kafka/topic-throughput/producer: post: description: "Returns the producer topic-throughput overview for the\nwindow \u2014 what each producer service wrote,\ \ per topic." operationId: post_o11y_messaging-queues_kafka_topic-throughput_producer requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueryRangeOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the producer topic-throughput overview for the window \u2014 what each producer service wrote, per\ \ topic." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/messaging-queues/kafka/topic-throughput/producer-details: post: description: 'Breaks one producer topic''s throughput down using the topic and service named in variables.' operationId: post_o11y_messaging-queues_kafka_topic-throughput_producer-details requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueryRangeOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Breaks one producer topic's throughput down using the topic and service named in variables. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/messaging-queues/queue-overview: post: description: "Lists the messaging destinations observed in the window \u2014 one\nrow per queue/destination/service\ \ combination with its throughput and\nlatency columns. Filters narrow by queue system, destination, service or any\n\ span attribute." operationId: post_o11y_messaging-queues_queue-overview requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueListIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueueRowsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the messaging destinations observed in the window \u2014 one row per queue/destination/service combination\ \ with its throughput and latency columns." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/metric/metric_metadata: get: description: "Serves the OLDER /metric/metric_metadata route. It is\nNOT the same op as metrics.go's metricMetadata\ \ (/metrics/metadata): different\npath, different input (this one also scopes by service). Two slices named one\n\ Go function for two routes; the route is the identity, so the name follows it.\nRenamed rather than merged \u2014\ \ collapsing them would silently drop the service\nscope this one accepts.\nIt returns one metric's metadata \u2014\ \ its type, unit, description,\ntemporality, monotonicity and histogram buckets \u2014 optionally scoped to the\n\ metric as one service reports it.\n\nCallers need the viewer role; the runtime's own gate enforces it." operationId: get_o11y_metric_metric_metadata parameters: - description: MetricName is the metric to read. in: query name: metricName required: false schema: type: string - description: ServiceName scopes the metadata to the metric as one service reports it. in: query name: serviceName required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricMetadataOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Serves the OLDER /metric/metric_metadata route. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/metric_reduction_rules: get: description: 'Lists the org''s metric volume-control (label reduction) rules, pageable and sortable by name, volume or recency.' operationId: ListMetricReductionRules parameters: - description: 'OrderBy sorts the page: metric, ingested_volume, reduced_volume or last_updated. Unset means ingested_volume.' in: query name: orderBy required: false schema: type: string - description: Order is asc or desc. Unset means desc. in: query name: order required: false schema: type: string - description: Search narrows the page to rules whose metric name contains it. in: query name: search required: false schema: type: string - description: MetricName narrows the page to one metric's rule. in: query name: metricName required: false schema: type: string - description: Offset is how many rules to skip, for paging. in: query name: offset required: false schema: format: int64 type: integer - description: Limit caps how many rules come back, at most 1000. Unset means 10. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yReductionRuleListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the org's metric volume-control (label reduction) rules, pageable and sortable by name, volume or recency. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Creates a volume-control rule for a metric and returns it with its id; a metric that already has a rule is refused.' operationId: CreateMetricReductionRule requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yReductionRuleCreateIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yReductionRuleOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a volume-control rule for a metric and returns it with its id; a metric that already has a rule is refused. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/metric_reduction_rules/preview: post: description: 'Estimates the series reduction and the dashboards and alerts a candidate volume-control rule would touch, without persisting it.' operationId: PreviewMetricReductionRule requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yReductionRulePreviewIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yReductionRulePreviewOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Estimates the series reduction and the dashboards and alerts a candidate volume-control rule would touch, without persisting it. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/metric_reduction_rules/stats: get: description: 'Returns total ingested vs retained series and samples and the estimated monthly savings across all volume-control rules.' operationId: GetMetricReductionRuleStats responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yReductionStatsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns total ingested vs retained series and samples and the estimated monthly savings across all volume-control rules. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/metric_reduction_rules/timeseries: get: description: 'Returns ingested vs retained series over time across all volume-control rules, in hourly buckets, in the query-range time-series response shape.' operationId: GetMetricReductionRuleTimeseries responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yReductionSeriesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns ingested vs retained series over time across all volume-control rules, in hourly buckets, in the query-range time-series response shape. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/metric_reduction_rules/{id}: delete: description: Deletes a volume-control rule by its id. operationId: DeleteMetricReductionRuleByID parameters: - description: ID is the rule's id. in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes a volume-control rule by its id. tags: - o11y x-app: o11y x-public: true x-tool: true get: description: Returns one volume-control rule by its id. operationId: GetMetricReductionRuleByID parameters: - description: ID is the rule's id. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yReductionRuleOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one volume-control rule by its id. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: 'Updates the match type and labels of a volume-control rule by its id; the metric name is immutable.' operationId: UpdateMetricReductionRuleByID parameters: - description: ID is the rule's id. in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yReductionRuleSaveIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yReductionRuleOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Updates the match type and labels of a volume-control rule by its id; the metric name is immutable. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/metrics: get: description: 'Lists the distinct metric names seen in a time range, each with its description, type, unit, temporality and monotonicity.' operationId: ListMetrics parameters: - description: Start is the start of the window as a Unix timestamp in milliseconds. in: query name: start required: false schema: format: int64 type: integer - description: End is the end of the window as a Unix timestamp in milliseconds. in: query name: end required: false schema: format: int64 type: integer - description: Limit caps how many metrics come back; unset means 100, at most 5000. in: query name: limit required: false schema: format: int64 type: integer - description: SearchText narrows the page to metric names containing it. in: query name: searchText required: false schema: type: string - description: Source narrows the page by ingestion source. in: query name: source required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the distinct metric names seen in a time range, each with its description, type, unit, temporality and monotonicity. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/metrics/alerts: get: description: Lists the alert rules that reference a metric. operationId: GetMetricAlerts parameters: - description: 'MetricName is the metric''s name; it may contain slashes, e.g. run.googleapis.com/request_latencies. Required.' in: query name: metricName required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricAlertsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the alert rules that reference a metric. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/metrics/attributes: get: description: 'Returns one metric''s attribute keys, each with its unique values and their count.' operationId: GetMetricAttributes parameters: - description: MetricName is the metric's name; it may contain slashes. Required. in: query name: metricName required: true schema: type: string - description: Start is the start of the window as a Unix timestamp in milliseconds. in: query name: start required: false schema: format: int64 type: integer - description: End is the end of the window as a Unix timestamp in milliseconds. in: query name: end required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricAttributesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one metric's attribute keys, each with its unique values and their count. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/metrics/dashboards: get: description: Lists the dashboard panels that reference a metric. operationId: GetMetricDashboardsV2 parameters: - description: 'MetricName is the metric''s name; it may contain slashes, e.g. run.googleapis.com/request_latencies. Required.' in: query name: metricName required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricDashboardsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the dashboard panels that reference a metric. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/metrics/highlights: get: description: 'Returns one metric''s headline numbers: data points, total and active time series, and when it was last received.' operationId: GetMetricHighlights parameters: - description: 'MetricName is the metric''s name; it may contain slashes, e.g. run.googleapis.com/request_latencies. Required.' in: query name: metricName required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricHighlightsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one metric''s headline numbers: data points, total and active time series, and when it was last received.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/metrics/inspect: post: description: "Returns one metric's raw time series over a window of at most\nthirty minutes \u2014 each series with\ \ its labels and timestamp/value pairs." operationId: InspectMetrics requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricInspectIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricInspectOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one metric's raw time series over a window of at most thirty minutes \u2014 each series with its labels\ \ and timestamp/value pairs." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/metrics/metadata: get: description: 'Returns one metric''s metadata: description, type, unit, temporality and monotonicity.' operationId: GetMetricMetadata parameters: - description: 'MetricName is the metric''s name; it may contain slashes, e.g. run.googleapis.com/request_latencies. Required.' in: query name: metricName required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricMetadataOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one metric''s metadata: description, type, unit, temporality and monotonicity.' tags: - o11y x-app: o11y x-public: true x-tool: true post: description: "Updates one metric's metadata \u2014 description, type, unit,\ntemporality, monotonicity \u2014 and answers\ \ with the bare success envelope." operationId: UpdateMetricMetadata requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricMetadataSaveIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricAckOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Updates one metric's metadata \u2014 description, type, unit, temporality, monotonicity \u2014 and answers\ \ with the bare success envelope." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/metrics/onboarding: get: description: "Reports whether any non-O11y metrics have been ingested \u2014\nthe lightweight check onboarding polls." operationId: GetMetricsOnboardingStatus responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricOnboardingOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Reports whether any non-O11y metrics have been ingested \u2014 the lightweight check onboarding polls." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/metrics/stats: post: description: "Lists metrics with their sample and time-series counts for a\ntime range \u2014 the volume view of the\ \ metrics explorer, pageable and sortable." operationId: GetMetricsStats requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricStatsIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricStatsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists metrics with their sample and time-series counts for a time range \u2014 the volume view of the metrics\ \ explorer, pageable and sortable." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/metrics/treemap: post: description: 'Returns the proportional distribution of metrics by sample count or time-series count, as the entries of a treemap.' operationId: GetMetricsTreemap requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricTreemapIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricTreemapOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the proportional distribution of metrics by sample count or time-series count, as the entries of a treemap. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/namespaces/attribute_keys: get: description: 'Lists the metric attribute keys Kubernetes namespaces report, for building namespace filters.' operationId: get_o11y_namespaces_attribute_keys parameters: - description: "DataSource is the telemetry the keys come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the keys will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the keys must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: SearchText narrows the keys to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the keys to one kind \u2014 tag or resource. Empty means all;\nan invalid value reads\ \ as empty." in: query name: tagType required: false schema: type: string - description: Limit caps how many keys come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the metric attribute keys Kubernetes namespaces report, for building namespace filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/namespaces/attribute_values: get: description: 'Lists the values one namespace attribute key has taken, for building namespace filters.' operationId: get_o11y_namespaces_attribute_values parameters: - description: "DataSource is the telemetry the values come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the values will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the values must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: AttributeKey is the key whose values to list. in: query name: attributeKey required: false schema: type: string - description: "FilterAttributeKeyDataType is the key's data type \u2014 string, int64,\nfloat64 or bool. Empty means\ \ unspecified." in: query name: filterAttributeKeyDataType required: false schema: type: string - description: SearchText narrows the values to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the search to one kind of key \u2014 tag or resource." in: query name: tagType required: false schema: type: string - description: Limit caps how many values come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeValuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the values one namespace attribute key has taken, for building namespace filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/namespaces/list: post: description: 'Lists Kubernetes namespaces over a time range, each with the CPU and memory its pods used, their phase counts and its attributes; filterable, groupable and paginated.' operationId: post_o11y_namespaces_list requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.NamespaceListRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yNamespaceListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes namespaces over a time range, each with the CPU and memory its pods used, their phase counts and its attributes; filterable, groupable and paginated. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/nextPrevErrorIDs: get: description: "Returns the ids of the exception instances immediately after\nand before a given one within its group\ \ \u2014 the paging cursor the error detail\nview walks.\n\nCallers need the viewer role; the runtime's own gate enforces\ \ it." operationId: get_o11y_nextpreverrorids parameters: - description: 'Timestamp is the instance''s time as a nanosecond epoch spelled as a string. Required.' in: query name: timestamp required: true schema: type: string - description: GroupID is the exception group the instance belongs to. Required. in: query name: groupID required: true schema: type: string - description: 'ErrorID is the exception instance id. Required by errorFromErrorID and nextPrevErrorIDs; unused by errorFromGroupID.' in: query name: errorID required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yNextPrevErrorIDs' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the ids of the exception instances immediately after and before a given one within its group \u2014\ \ the paging cursor the error detail view walks." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/nodes/attribute_keys: get: description: 'Lists the metric attribute keys Kubernetes nodes report, for building node filters.' operationId: get_o11y_nodes_attribute_keys parameters: - description: "DataSource is the telemetry the keys come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the keys will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the keys must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: SearchText narrows the keys to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the keys to one kind \u2014 tag or resource. Empty means all;\nan invalid value reads\ \ as empty." in: query name: tagType required: false schema: type: string - description: Limit caps how many keys come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the metric attribute keys Kubernetes nodes report, for building node filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/nodes/attribute_values: get: description: 'Lists the values one node attribute key has taken, for building node filters.' operationId: get_o11y_nodes_attribute_values parameters: - description: "DataSource is the telemetry the values come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the values will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the values must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: AttributeKey is the key whose values to list. in: query name: attributeKey required: false schema: type: string - description: "FilterAttributeKeyDataType is the key's data type \u2014 string, int64,\nfloat64 or bool. Empty means\ \ unspecified." in: query name: filterAttributeKeyDataType required: false schema: type: string - description: SearchText narrows the values to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the search to one kind of key \u2014 tag or resource." in: query name: tagType required: false schema: type: string - description: Limit caps how many values come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeValuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the values one node attribute key has taken, for building node filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/nodes/list: post: description: 'Lists Kubernetes nodes over a time range, each with its CPU and memory usage against allocatable capacity, readiness condition counts and attributes; filterable, groupable and paginated.' operationId: post_o11y_nodes_list requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.NodeListRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yNodeListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes nodes over a time range, each with its CPU and memory usage against allocatable capacity, readiness condition counts and attributes; filterable, groupable and paginated. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/org/preferences: get: description: 'Lists every org-scoped preference, each with its current and default value. Admin gate.' operationId: ListOrgPreferences responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yPreferencesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists every org-scoped preference, each with its current and default value. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/org/preferences/{name}: get: description: Returns one org-scoped preference, by name. Admin gate. operationId: GetOrgPreference parameters: - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yPreferenceOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one org-scoped preference, by name. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: Sets one org-scoped preference, by name. Admin gate. operationId: UpdateOrgPreference parameters: - in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yUpdatablePreference' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Sets one org-scoped preference, by name. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/orgs/me: get: description: Returns the caller's own organization. Admin gate. operationId: GetMyOrganization responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yOrganizationOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the caller's own organization. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: "Rewrites the caller's own organization record \u2014 display name,\nname, alias \u2014 always addressed\ \ as \"me\", never by id. Admin gate." operationId: UpdateMyOrganization requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yOrganization' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Rewrites the caller's own organization record \u2014 display name, name, alias \u2014 always addressed as\ \ \"me\", never by id." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/orgs/me/filters: get: description: "Returns the org's quick filters for every signal \u2014 the\nattribute shortlists its explorers offer\ \ as one-click filters. Viewer gate." operationId: GetQuickFilters responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQuickFiltersOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the org's quick filters for every signal \u2014 the attribute shortlists its explorers offer as one-click\ \ filters." tags: - o11y x-app: o11y x-public: true x-tool: true put: description: 'Replaces the org''s quick filters for one signal with the attribute list given. Admin gate.' operationId: UpdateQuickFilters requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yUpdatableQuickFilters' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Replaces the org's quick filters for one signal with the attribute list given. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/orgs/me/filters/{signal}: get: description: "Returns the org's quick filters for one signal \u2014 traces,\nlogs, metrics, exceptions or api_monitoring.\ \ Viewer gate." operationId: GetSignalFilters parameters: - in: path name: signal required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySignalFiltersOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the org's quick filters for one signal \u2014 traces, logs, metrics, exceptions or api_monitoring." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/pods/attribute_keys: get: description: 'Lists the metric attribute keys Kubernetes pods report, for building pod filters.' operationId: get_o11y_pods_attribute_keys parameters: - description: "DataSource is the telemetry the keys come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the keys will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the keys must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: SearchText narrows the keys to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the keys to one kind \u2014 tag or resource. Empty means all;\nan invalid value reads\ \ as empty." in: query name: tagType required: false schema: type: string - description: Limit caps how many keys come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the metric attribute keys Kubernetes pods report, for building pod filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/pods/attribute_values: get: description: 'Lists the values one pod attribute key has taken, for building pod filters.' operationId: get_o11y_pods_attribute_values parameters: - description: "DataSource is the telemetry the values come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the values will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the values must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: AttributeKey is the key whose values to list. in: query name: attributeKey required: false schema: type: string - description: "FilterAttributeKeyDataType is the key's data type \u2014 string, int64,\nfloat64 or bool. Empty means\ \ unspecified." in: query name: filterAttributeKeyDataType required: false schema: type: string - description: SearchText narrows the values to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the search to one kind of key \u2014 tag or resource." in: query name: tagType required: false schema: type: string - description: Limit caps how many values come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeValuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the values one pod attribute key has taken, for building pod filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/pods/list: post: description: 'Lists Kubernetes pods over a time range, each with its CPU and memory usage against request and limit, restart count, phase counts and attributes; filterable, groupable and paginated.' operationId: post_o11y_pods_list requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PodListRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yPodListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes pods over a time range, each with its CPU and memory usage against request and limit, restart count, phase counts and attributes; filterable, groupable and paginated. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/processes/attribute_keys: get: description: 'Lists the metric attribute keys processes report, for building process filters.' operationId: get_o11y_processes_attribute_keys parameters: - description: "DataSource is the telemetry the keys come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the keys will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the keys must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: SearchText narrows the keys to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the keys to one kind \u2014 tag or resource. Empty means all;\nan invalid value reads\ \ as empty." in: query name: tagType required: false schema: type: string - description: Limit caps how many keys come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the metric attribute keys processes report, for building process filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/processes/attribute_values: get: description: 'Lists the values one process attribute key has taken, for building process filters.' operationId: get_o11y_processes_attribute_values parameters: - description: "DataSource is the telemetry the values come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the values will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the values must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: AttributeKey is the key whose values to list. in: query name: attributeKey required: false schema: type: string - description: "FilterAttributeKeyDataType is the key's data type \u2014 string, int64,\nfloat64 or bool. Empty means\ \ unspecified." in: query name: filterAttributeKeyDataType required: false schema: type: string - description: SearchText narrows the values to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the search to one kind of key \u2014 tag or resource." in: query name: tagType required: false schema: type: string - description: Limit caps how many values come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeValuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the values one process attribute key has taken, for building process filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/processes/list: post: description: 'Lists monitored processes over a time range, each with its name, PID, command line and CPU and memory usage; filterable, groupable and paginated.' operationId: post_o11y_processes_list requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.ProcessListRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yProcessListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists monitored processes over a time range, each with its name, PID, command line and CPU and memory usage; filterable, groupable and paginated. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/product/metrics: get: description: "Returns one product's RED series \u2014 request rate, errors, p50\nand p95 latency \u2014 for the caller's\ \ org, plus that org's LLM usage rollup over\nthe same window. The series come from org-tagged request spans, so a\ \ tenant\nonly ever aggregates its own traffic; a validated platform SuperAdmin sees the\nwhole product's RED, while\ \ usage stays the caller's own org either way. A\nwell-formed product with no backing workload answers empty series;\ \ a malformed\nslug is a 400." operationId: get_o11y_product_metrics parameters: - description: Product is the console product slug to read, e.g. "kms". Required. example: kms in: query name: product required: false schema: type: string - description: Range is the window in seconds. Default 3600, capped at 604800 (7d). example: 3600 in: query name: range required: false schema: format: int64 type: integer - description: 'StepSec is the bucket width in seconds, clamped to [30, 3600]. Absent picks ~60 buckets across the range.' in: query name: stepSec required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.metricsResponse' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one product's RED series \u2014 request rate, errors, p50 and p95 latency \u2014 for the caller's\ \ org, plus that org's LLM usage rollup over the same window." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/public/dashboards/{id}: get: description: "Returns the sanitized dashboard data for public access \u2014\nthe read a shared dashboard's public page\ \ makes.\n\nAnonymous, scoped to the public dashboard's read scope; the runtime's own gate\nenforces it." operationId: GetPublicDashboardData parameters: - description: ID is the resource id from the path. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yPublicDashboardDataOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the sanitized dashboard data for public access \u2014 the read a shared dashboard's public page makes." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/public/dashboards/{id}/widgets/{idx}/query_range: get: description: 'Returns the query-range result for one widget of a public dashboard. When the share fixes its own time range the caller''s startTime/endTime are ignored; otherwise they bound the window as millisecond epochs. Anonymous, scoped to the public dashboard''s read scope; the runtime''s own gate enforces it.' operationId: GetPublicDashboardWidgetQueryRange parameters: - description: ID is the public dashboard id from the path. in: path name: id required: true schema: type: string - description: Idx is the widget's index from the path. in: path name: idx required: true schema: type: string - description: 'StartTime is the window start as a millisecond epoch. Used only when the share enables a caller-chosen time range.' in: query name: startTime required: false schema: type: string - description: 'EndTime is the window end as a millisecond epoch. Used only when the share enables a caller-chosen time range.' in: query name: endTime required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yWidgetQueryRangeOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the query-range result for one widget of a public dashboard. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/pvcs/attribute_keys: get: description: 'Lists the metric attribute keys persistent volume claims report, for building volume filters.' operationId: get_o11y_pvcs_attribute_keys parameters: - description: "DataSource is the telemetry the keys come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the keys will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the keys must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: SearchText narrows the keys to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the keys to one kind \u2014 tag or resource. Empty means all;\nan invalid value reads\ \ as empty." in: query name: tagType required: false schema: type: string - description: Limit caps how many keys come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the metric attribute keys persistent volume claims report, for building volume filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/pvcs/attribute_values: get: description: 'Lists the values one persistent-volume-claim attribute key has taken, for building volume filters.' operationId: get_o11y_pvcs_attribute_values parameters: - description: "DataSource is the telemetry the values come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the values will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the values must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: AttributeKey is the key whose values to list. in: query name: attributeKey required: false schema: type: string - description: "FilterAttributeKeyDataType is the key's data type \u2014 string, int64,\nfloat64 or bool. Empty means\ \ unspecified." in: query name: filterAttributeKeyDataType required: false schema: type: string - description: SearchText narrows the values to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the search to one kind of key \u2014 tag or resource." in: query name: tagType required: false schema: type: string - description: Limit caps how many values come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeValuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the values one persistent-volume-claim attribute key has taken, for building volume filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/pvcs/list: post: description: 'Lists Kubernetes persistent volume claims over a time range, each with its available, capacity and used bytes, inode counts and attributes; filterable, groupable and paginated.' operationId: post_o11y_pvcs_list requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.VolumeListRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yPvcListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes persistent volume claims over a time range, each with its available, capacity and used bytes, inode counts and attributes; filterable, groupable and paginated. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/query: get: description: "Evaluates one instant PromQL query against the org's metrics and\nreturns the result at a single point\ \ in time.\n\nThe result is polymorphic by PromQL's own contract \u2014 a matrix, vector,\nscalar or string, discriminated\ \ by resultType \u2014 so it is carried verbatim\nrather than forced into one of its shapes.\n\nCallers need the viewer\ \ role; the runtime's own gate enforces it." operationId: get_o11y_query parameters: - description: Query is the PromQL expression to evaluate. Required. in: query name: query required: true schema: type: string - description: "Time is the evaluation timestamp \u2014 epoch seconds or RFC3339. Empty\nevaluates at now." in: query name: time required: false schema: type: string - description: Stats set to any non-empty value includes query statistics in the answer. in: query name: stats required: false schema: type: string - description: Timeout caps evaluation time, as a duration in seconds. in: query name: timeout required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yPromQueryOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Evaluates one instant PromQL query against the org's metrics and returns the result at a single point in time. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/query_filter/analyze: post: description: 'Analyzes a query and extracts the metric names it reads and the columns it groups by. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: post_o11y_query_filter_analyze requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAnalyzeIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAnalyzeOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Analyzes a query and extracts the metric names it reads and the columns it groups by. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/query_progress: get: description: "Reports how far a submitted query has got \u2014 rows scanned, bytes read, elapsed \u2014 and HOLDS the\ \ connection until the next update rather than answering immediately.\n\nONE ADDRESS, TWO PROTOCOLS. Send an Upgrade\ \ and this is a websocket carrying the same progress; send an ordinary GET and it is a long poll. The Upgrade is a\ \ property of the request, not of the address, so the read that used to answer at /ws/query_progress answers here.\n\ \nThe long poll is the whole point, and the reason this cannot be a typed operation: an answer that arrived only when\ \ the query finished would report progress on nothing, and an upgraded connection has no JSON response to declare.\n\ \nA validated, org-scoped principal is required; a query id belonging to another tenant is simply not found." operationId: get_o11y_query_progress summary: Watch one running query's progress tags: - o11y x-app: o11y x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/o11y/query_range: get: description: "Runs a Prometheus-style range query over metrics \u2014 the\nlegacy read that predates the v5 querier\ \ \u2014 and returns the matrix, vector or\nscalar the query resolved to.\n\nCallers need the viewer role; the runtime's\ \ own gate enforces it." operationId: get_o11y_query_range parameters: - description: "Start is the window start \u2014 a unix timestamp (seconds, with optional\nfraction) or an RFC 3339\ \ time. Required." in: query name: start required: true schema: type: string - description: 'End is the window end, in the same form as Start, and not before it. Required.' in: query name: end required: true schema: type: string - description: "Step is the query resolution, e.g. 60s, 1m, 1h \u2014 a positive duration.\nRequired." in: query name: step required: true schema: type: string - description: Query is the PromQL expression to evaluate. Required. in: query name: query required: true schema: type: string - description: Stats, when "all", asks for query statistics alongside the result. in: query name: stats required: false schema: type: string - description: "Timeout caps how long the query may run, e.g. 30s, 1m \u2014 a positive\nduration. Absent means the\ \ server default." in: query name: timeout required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMetricsQueryRangeOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Runs a Prometheus-style range query over metrics \u2014 the legacy read that predates the v5 querier \u2014\ \ and returns the matrix, vector or scalar the query resolved to." tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Executes a composite query over a time range: builder queries over traces, logs and metrics, formulas, trace operators, PromQL and Datastore SQL, answering time series, scalars or raw records as the request type asks. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: post_o11y_query_range requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.QueryRangeRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueryRangeOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Executes a composite query over a time range: builder queries over traces, logs and metrics, formulas, trace operators, PromQL and Datastore SQL, answering time series, scalars or raw records as the request type asks.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/query_range/format: post: description: "Parses a builder query and echoes it back normalized to the\nv3 shape \u2014 the endpoint the UI uses\ \ to canonicalize a query without running\nit.\n\nCallers need the viewer role; the runtime's own gate enforces it." operationId: post_o11y_query_range_format requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.QueryRangeParamsV3' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueryRangeFormatOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Parses a builder query and echoes it back normalized to the v3 shape \u2014 the endpoint the UI uses to canonicalize\ \ a query without running it." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/query_range/preview: post: description: "Validates a composite query and renders the Datastore\nstatements it would run WITHOUT executing it \u2014\ \ a dry run for agentic and\ntooling use. verbose=false trades the rendered SQL and EXPLAIN for a\nlightweight per-query\ \ verdict.\n\nCallers need the viewer role; the runtime's own gate enforces it." operationId: post_o11y_query_range_preview requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueryRangePreviewIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yQueryRangePreviewOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Validates a composite query and renders the Datastore statements it would run WITHOUT executing it \u2014\ \ a dry run for agentic and tooling use." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/readyz: get: description: "Reports whether the runtime's registered services are healthy enough to take traffic, and answers 503\ \ when they are not \u2014 which is what takes a booting or degraded replica out of the load balancer instead of letting\ \ it serve errors.\n\nUNAUTHENTICATED by design, like the other two probes. It reads the same service registry the\ \ health probe reads, so the two agree by construction; readiness is the question a router asks and health is the\ \ question an operator asks." operationId: get_o11y_readyz summary: Readiness of the observability runtime to serve tags: - o11y x-app: o11y x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/o11y/reviews: get: description: 'Returns a page of the caller org''s human-review queues, newest first, narrowed to the caller''s project. Another org''s queues are never visible.' operationId: get_o11y_reviews parameters: - description: Page is the 1-based page to read. Default 1. example: 1 in: query name: page required: false schema: format: int64 type: integer - description: Limit is how many rows to return. Default 20, capped at 100. example: 20 in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.annQueueList' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns a page of the caller org's human-review queues, newest first, narrowed to the caller's project. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Creates a human-review queue in the caller''s org and project. A name already used by another queue in the same project is a 409.' operationId: post_o11y_reviews requestBody: content: application/json: example: name: hallucination review scoreConfigIds: - quality schema: $ref: '#/components/schemas/o11y.createQueueReq' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.annQueueView' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a human-review queue in the caller's org and project. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/reviews/{id}: delete: description: 'Removes one review queue and every item in it. A queue id belonging to another org answers the same 404 an unknown id does, so a probe learns nothing about what exists.' operationId: delete_o11y_reviews_by_id parameters: - description: ID is the annotation queue to act on, from the path. example: annq_1 in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.annQueueDeleted' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one review queue and every item in it. tags: - o11y x-app: o11y x-public: true x-tool: true get: description: 'Returns one review queue with its pending and completed counts and its first page of items. A queue id belonging to another org is a 404, never a cross-tenant read.' operationId: get_o11y_reviews_by_id parameters: - description: ID is the annotation queue to act on, from the path. example: annq_1 in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.annQueueDetailView' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one review queue with its pending and completed counts and its first page of items. tags: - o11y x-app: o11y x-public: true x-tool: true patch: description: 'Changes a review queue''s name, description or score-config set. A field the request omits is left alone. A name another queue in the same project already uses is a 409; a queue id belonging to another org is a 404.' operationId: patch_o11y_reviews_by_id parameters: - description: ID is the annotation queue to update, from the path. example: annq_1 in: path name: id required: true schema: type: string requestBody: content: application/json: example: id: annq_1 name: hallucination review v2 schema: $ref: '#/components/schemas/o11y.updateQueueIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.annQueueView' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes a review queue's name, description or score-config set. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/reviews/{id}/items: get: description: 'Returns a page of one review queue''s items, newest first, optionally filtered to PENDING or COMPLETED. A queue id belonging to another org is a 404, never a cross-tenant list.' operationId: get_o11y_reviews_by_id_items parameters: - description: ID is the annotation queue whose items to list, from the path. example: annq_1 in: path name: id required: true schema: type: string - description: Status filters to PENDING or COMPLETED items. Absent returns both. example: PENDING in: query name: status required: false schema: type: string - description: Page is the 1-based page to read. Default 1. in: query name: page required: false schema: format: int64 type: integer - description: Limit is how many rows to return. Default 20, capped at 100. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.annItemList' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns a page of one review queue's items, newest first, optionally filtered to PENDING or COMPLETED. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Enqueues traces, observations or sessions on a review queue. Each item names exactly one object, either by traceId / observationId / sessionId or by objectType plus objectId; every item enters PENDING. A queue id belonging to another org is a 404.' operationId: post_o11y_reviews_by_id_items parameters: - description: ID is the annotation queue to add to, from the path. example: annq_1 in: path name: id required: true schema: type: string requestBody: content: application/json: example: id: annq_1 items: - traceId: tr_1 schema: $ref: '#/components/schemas/o11y.addItemsIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.annItemsCreated' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Enqueues traces, observations or sessions on a review queue. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/reviews/{id}/items/{itemId}: patch: description: 'Moves one queue item between PENDING and COMPLETED and sets its assignee. Completing an item stamps its completedAt. An item that exists under a different queue answers the same 404 an unknown item does, and so does a queue belonging to another org.' operationId: patch_o11y_reviews_by_id_items_by_itemid parameters: - description: ID is the annotation queue the item belongs to, from the path. example: annq_1 in: path name: id required: true schema: type: string - description: ItemID is the item to update, from the path. example: annqi_1 in: path name: itemId required: true schema: type: string requestBody: content: application/json: example: id: annq_1 itemId: annqi_1 status: COMPLETED schema: $ref: '#/components/schemas/o11y.updateItemIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.annItemView' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Moves one queue item between PENDING and COMPLETED and sets its assignee. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/roles: get: description: "Lists every role in the caller's org \u2014 the managed ones the\nplatform seeds and the custom ones its\ \ admins created." operationId: ListRoles responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRolesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists every role in the caller's org \u2014 the managed ones the platform seeds and the custom ones its admins\ \ created." tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Creates a custom role in the caller''s org from a name, an optional description and the transaction groups it grants, answering the new role''s id. Names are lowercase letters and hyphens only, and may not start with the reserved managed-role prefix; the runtime refuses anything else.' operationId: CreateRole requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRoleCreateIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRoleCreateOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a custom role in the caller's org from a name, an optional description and the transaction groups it grants, answering the new role's id. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/roles/{id}: delete: description: 'Deletes a custom role. A role that still has user or service-account assignees, or an auth-domain mapping, is refused; managed roles cannot be deleted.' operationId: DeleteRole parameters: - in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes a custom role. tags: - o11y x-app: o11y x-public: true x-tool: true get: description: Returns one role with the transaction groups it grants. operationId: GetRole parameters: - in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRoleOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one role with the transaction groups it grants. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: "Replaces a custom role's description and transaction groups.\nBoth fields are mandatory \u2014 send an\ \ empty string or an empty array to clear\none \u2014 and managed roles cannot be edited." operationId: UpdateRole parameters: - in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRoleUpdateIn' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Replaces a custom role's description and transaction groups. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/route_policies: get: description: Lists the org's route policies. Viewer gate. operationId: GetAllRoutePolicies responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRoutePoliciesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the org's route policies. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: Creates a route policy, answering with the stored policy. Admin gate. operationId: CreateRoutePolicy requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostableRoutePolicy' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRoutePolicyOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a route policy, answering with the stored policy. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/route_policies/{id}: delete: description: Removes a route policy, by id. Admin gate. operationId: DeleteRoutePolicyByID parameters: - in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a route policy, by id. tags: - o11y x-app: o11y x-public: true x-tool: true get: description: Returns one route policy, by id. Viewer gate. operationId: GetRoutePolicyByID parameters: - in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRoutePolicyOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one route policy, by id. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: 'Replaces a route policy, by id, answering with the stored policy. Admin gate.' operationId: UpdateRoutePolicy parameters: - in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRoutePolicyUpdateIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRoutePolicyOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Replaces a route policy, by id, answering with the stored policy. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/rules: get: description: Lists all alert rules with their current evaluation state. Viewer gate. operationId: ListRules responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRulesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists all alert rules with their current evaluation state. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: Creates a new alert rule and answers with the stored rule. Editor gate. operationId: CreateRule requestBody: content: application/json: schema: {} required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRuleOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a new alert rule and answers with the stored rule. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/rules/test: post: description: 'Fires a test notification for a rule definition without saving it, answering with how many series would alert. Editor gate.' operationId: TestRule requestBody: content: application/json: schema: {} required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yTestRuleOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Fires a test notification for a rule definition without saving it, answering with how many series would alert. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/rules/{id}: delete: description: Removes an alert rule, by id. Editor gate. operationId: DeleteRuleByID parameters: - in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes an alert rule, by id. tags: - o11y x-app: o11y x-public: true x-tool: true get: description: Returns one alert rule with its evaluation state, by id. Viewer gate. operationId: GetRuleByID parameters: - in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRuleOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one alert rule with its evaluation state, by id. tags: - o11y x-app: o11y x-public: true x-tool: true patch: description: "Applies a partial update to an alert rule, by id, answering\nwith the stored rule \u2014 the common toggle\ \ for enabling or muting a rule.\nEditor gate." operationId: PatchRuleByID parameters: - in: path name: id required: true schema: type: string requestBody: content: application/json: schema: {} required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRuleOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Applies a partial update to an alert rule, by id, answering with the stored rule \u2014 the common toggle\ \ for enabling or muting a rule." tags: - o11y x-app: o11y x-public: true x-tool: true put: description: Replaces an alert rule's definition, by id. Editor gate. operationId: UpdateRuleByID parameters: - in: path name: id required: true schema: type: string requestBody: content: application/json: schema: {} required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Replaces an alert rule's definition, by id. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/rules/{id}/history/filter_keys: get: description: 'Returns the distinct label keys present in a rule''s history entries over the selected range, for building history filters. Viewer gate.' operationId: GetRuleHistoryFilterKeys parameters: - in: path name: id required: true schema: type: string - description: StartUnixMilli is the window start, unix milliseconds. in: query name: startUnixMilli required: false schema: format: int64 type: integer - description: EndUnixMilli is the window end, unix milliseconds. in: query name: endUnixMilli required: false schema: format: int64 type: integer - description: SearchText narrows the keys to those containing it. in: query name: searchText required: false schema: type: string - description: Limit caps how many keys come back. Absent means 50, capped at 200. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRuleHistoryFilterKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the distinct label keys present in a rule's history entries over the selected range, for building history filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/rules/{id}/history/filter_values: get: description: 'Returns the distinct values a given label key has taken across a rule''s history entries. Viewer gate.' operationId: GetRuleHistoryFilterValues parameters: - in: path name: id required: true schema: type: string - in: query name: startUnixMilli required: false schema: format: int64 type: integer - in: query name: endUnixMilli required: false schema: format: int64 type: integer - in: query name: searchText required: false schema: type: string - in: query name: limit required: false schema: format: int64 type: integer - description: Name is the label key whose values to list. Required. in: query name: name required: true schema: type: string - description: ExistingQuery is a filter expression scoping which values appear. in: query name: existingQuery required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRuleHistoryFilterValuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the distinct values a given label key has taken across a rule's history entries. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/rules/{id}/history/overall_status: get: description: 'Returns the overall firing/inactive intervals for a rule over the selected range. Viewer gate.' operationId: GetRuleHistoryOverallStatus parameters: - in: path name: id required: true schema: type: string - description: Start is the window start, unix milliseconds. Required by the runtime. in: query name: start required: false schema: format: int64 type: integer - description: End is the window end, unix milliseconds. Required by the runtime. in: query name: end required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRuleHistoryOverallStatusOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the overall firing/inactive intervals for a rule over the selected range. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Returns the overall firing/inactive windows for a rule, for the posted query range. Viewer gate.' operationId: GetOverallStateTransitions parameters: - in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRuleHistoryQueryIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yOverallStateTransitionsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the overall firing/inactive windows for a rule, for the posted query range. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/rules/{id}/history/stats: get: description: 'Returns trigger and resolution statistics for a rule over the selected time range, current window against the prior one. Viewer gate.' operationId: GetRuleHistoryStats parameters: - in: path name: id required: true schema: type: string - description: Start is the window start, unix milliseconds. Required by the runtime. in: query name: start required: false schema: format: int64 type: integer - description: End is the window end, unix milliseconds. Required by the runtime. in: query name: end required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRuleHistoryStatsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns trigger and resolution statistics for a rule over the selected time range, current window against the prior one. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Returns trigger and resolution statistics for a rule, current window against the prior one, for the posted query range. Viewer gate.' operationId: GetRuleStats parameters: - in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRuleHistoryQueryIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRuleStatsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns trigger and resolution statistics for a rule, current window against the prior one, for the posted query range. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/rules/{id}/history/timeline: get: description: 'Returns paginated timeline entries for a rule''s state transitions, filterable by state and a label expression, cursor-paginated. Viewer gate.' operationId: GetRuleHistoryTimeline parameters: - in: path name: id required: true schema: type: string - description: Start is the window start, unix milliseconds. Required by the runtime. in: query name: start required: false schema: format: int64 type: integer - description: End is the window end, unix milliseconds. Required by the runtime. in: query name: end required: false schema: format: int64 type: integer - description: State keeps only entries in one alert state, e.g. firing or normal. in: query name: state required: false schema: type: string - description: FilterExpression narrows entries to those whose labels match it. in: query name: filterExpression required: false schema: type: string - description: Limit caps how many entries come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer - description: Order sorts by time, asc or desc. in: query name: order required: false schema: type: string - description: Cursor resumes a previous page; opaque, returned as nextCursor. in: query name: cursor required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRuleHistoryTimelineOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns paginated timeline entries for a rule's state transitions, filterable by state and a label expression, cursor-paginated. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Returns a rule''s state-transition timeline for the posted query range, each entry carrying its related-logs or related-traces link. Viewer gate.' operationId: GetRuleStateHistory parameters: - in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRuleHistoryQueryIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRuleStateTimelineOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns a rule's state-transition timeline for the posted query range, each entry carrying its related-logs or related-traces link. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/rules/{id}/history/top_contributors: get: description: 'Returns the label combinations that contributed most to a rule firing over the selected range. Viewer gate.' operationId: GetRuleHistoryTopContributors parameters: - in: path name: id required: true schema: type: string - description: Start is the window start, unix milliseconds. Required by the runtime. in: query name: start required: false schema: format: int64 type: integer - description: End is the window end, unix milliseconds. Required by the runtime. in: query name: end required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRuleHistoryContributorsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the label combinations that contributed most to a rule firing over the selected range. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Returns the label combinations that contributed most to a rule firing, for the posted query range. Viewer gate.' operationId: GetRuleStateHistoryTopContributors parameters: - in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRuleHistoryQueryIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRuleStateContributorsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the label combinations that contributed most to a rule firing, for the posted query range. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/sentinel/discover: post: description: "Aggregates a project's captured errors into a table \u2014 the caller\nnames the filters, the groupings\ \ and the aggregations, and gets back the\ncolumns and rows they asked for.\n\nThe project is mandatory and is checked\ \ against the caller's own org before it\nscopes anything, so a project id belonging to someone else reads as absent\n\ rather than as data." operationId: post_o11y_sentinel_discover requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDiscoverIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDiscoverOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Aggregates a project's captured errors into a table \u2014 the caller names the filters, the groupings and\ \ the aggregations, and gets back the columns and rows they asked for." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/sentinel/events/{id}: get: description: 'Returns one captured error event of a project, by its id. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_sentinel_events_by_id parameters: - description: ID is the event id. in: path name: id required: true schema: type: string - description: Project is the project the event belongs to, by its id. Required. in: query name: project required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySentryEventOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one captured error event of a project, by its id. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/sentinel/issues: get: description: 'Lists the caller''s org''s grouped error issues, optionally narrowed to one project and one time window, and filtered by status, level, environment, service, a free-text query and a sort. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_sentinel_issues parameters: - description: 'Status narrows to one lifecycle state: unresolved, resolved or ignored.' in: query name: status required: false schema: type: string - description: Level narrows to one severity, e.g. error, warning, info. in: query name: level required: false schema: type: string - description: Environment narrows to one deployment environment. in: query name: environment required: false schema: type: string - description: ServiceName narrows to one reporting service. in: query name: serviceName required: false schema: type: string - description: Query narrows to issues whose text contains it. in: query name: query required: false schema: type: string - description: Sort orders the page, e.g. lastSeen, firstSeen, count. in: query name: sort required: false schema: type: string - description: Offset is how many issues to skip. Zero starts at the first. in: query name: offset required: false schema: format: int64 type: integer - description: Limit caps how many issues come back. Zero means the default. in: query name: limit required: false schema: format: int64 type: integer - description: Project narrows the org's issues to one project, by its id. in: query name: project required: false schema: type: string - description: "Period is the window to read, relative to now \u2014 1h, 24h, 7d, 14d, 30d." in: query name: period required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yErrorIssuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller's org's grouped error issues, optionally narrowed to one project and one time window, and filtered by status, level, environment, service, a free-text query and a sort. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/sentinel/issues/{id}: get: description: 'Returns one grouped issue of the caller''s org with its latest occurrence sample. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_sentinel_issues_by_id parameters: - description: ID is the issue id. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yErrorGettableIssueOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one grouped issue of the caller's org with its latest occurrence sample. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: "Changes an issue's lifecycle \u2014 resolve, ignore, reopen or\nassign \u2014 and returns the updated\ \ issue. Fields left unset are left unchanged.\n\nCallers need the editor role; the runtime's own gate enforces it." operationId: put_o11y_sentinel_issues_by_id parameters: - description: ID is the issue id. in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySentryUpdateIssueIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yErrorIssueOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Changes an issue's lifecycle \u2014 resolve, ignore, reopen or assign \u2014 and returns the updated issue." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/sentinel/issues/{id}/events: get: description: "Lists one issue's captured occurrences, scoped to a project\n\u2014 a project is an isolation unit, so\ \ the caller declares which project's\noccurrences to read.\n\nCallers need the viewer role; the runtime's own gate\ \ enforces it." operationId: get_o11y_sentinel_issues_by_id_events parameters: - description: ID is the issue id. in: path name: id required: true schema: type: string - description: Project is the project whose occurrences to read, by its id. Required. in: query name: project required: true schema: type: string - description: Limit caps how many occurrences come back. Zero means the default. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySentryIssueEventsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists one issue's captured occurrences, scoped to a project \u2014 a project is an isolation unit, so the\ \ caller declares which project's occurrences to read." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/sentinel/logs: get: description: 'Lists a project''s captured error events, newest first, optionally narrowed to those whose message or exception text contains a search string.' operationId: get_o11y_sentinel_logs parameters: - description: Project is the project to read, as its id. Required. in: query name: project required: true schema: type: string - description: Query narrows the page to events whose text contains it. in: query name: query required: false schema: type: string - description: "Period is the window to read, relative to now \u2014 1h, 24h, 7d, 14d, 30d." in: query name: period required: false schema: type: string - description: Limit caps how many events come back. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yLogsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists a project's captured error events, newest first, optionally narrowed to those whose message or exception text contains a search string. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/sentinel/projects: get: description: 'Lists the caller''s org''s Sentry projects, each with its freshly-derived DSN. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_sentinel_projects responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySentryProjectsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller's org's Sentry projects, each with its freshly-derived DSN. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Creates a Sentry project under the caller''s org and returns it, DSN included. Only the name, and optionally a slug and platform, are the caller''s to set; the org, id and key are server-assigned. Callers need the editor role; the runtime''s own gate enforces it.' operationId: post_o11y_sentinel_projects requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySentryPostableProject' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySentryProjectOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a Sentry project under the caller's org and returns it, DSN included. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/sentinel/projects/{id}: delete: description: 'Deletes one Sentry project of the caller''s org. Its DSN stops resolving immediately, so ingest for that id fails closed exactly as an unknown project does; retained events are not touched. Answers 204. Callers need the editor role; the runtime''s own gate enforces it.' operationId: delete_o11y_sentinel_projects_by_id parameters: - description: ID is the project id. in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes one Sentry project of the caller's org. tags: - o11y x-app: o11y x-public: true x-tool: true get: description: 'Returns one Sentry project of the caller''s org, DSN included. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_sentinel_projects_by_id parameters: - description: ID is the project id. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySentryProjectOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one Sentry project of the caller's org, DSN included. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/sentinel/projects/{id}/keys/rotate: post: description: "Rotates a project's DSN key \u2014 bumping its rotation\nwatermark so keys below it stop verifying \u2014\ \ and returns the project with its\nnew DSN.\n\nCallers need the editor role; the runtime's own gate enforces it." operationId: post_o11y_sentinel_projects_by_id_keys_rotate parameters: - description: ID is the project id. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySentryProjectOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Rotates a project's DSN key \u2014 bumping its rotation watermark so keys below it stop verifying \u2014 and\ \ returns the project with its new DSN." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/sentinel/stats: get: description: 'Returns a project''s event-rate timeseries: one bucket per interval over the requested period, counting the events in it.' operationId: get_o11y_sentinel_stats parameters: - description: Project is the project to read, as its id. Required. in: query name: project required: true schema: type: string - description: Field is the dimension to count over. Empty counts all events. in: query name: field required: false schema: type: string - description: "Period is the window to read, relative to now \u2014 1h, 24h, 7d, 14d, 30d." in: query name: period required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yStatsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns a project''s event-rate timeseries: one bucket per interval over the requested period, counting the events in it.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/sentinel/traces: get: description: "Lists the traces a project's captured errors reference, each with how\nmany errors landed on it, when\ \ they started and stopped, and the latest\nmessage seen \u2014 the entry point for \"which requests are failing\"\ ." operationId: get_o11y_sentinel_traces parameters: - description: Project is the project to read, as its id. Required. in: query name: project required: true schema: type: string - description: "Period is the window to read, relative to now \u2014 1h, 24h, 7d, 14d, 30d." in: query name: period required: false schema: type: string - description: Limit caps how many traces come back. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yTracesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the traces a project's captured errors reference, each with how many errors landed on it, when they\ \ started and stopped, and the latest message seen \u2014 the entry point for \"which requests are failing\"." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/sentinel/traces/{id}: get: description: "Returns one trace's captured errors for a project \u2014 every error event\nthat carried the trace id,\ \ in the order the events plane holds them." operationId: get_o11y_sentinel_traces_by_id parameters: - description: ID is the trace id. in: path name: id required: true schema: type: string - description: Project is the project the trace's errors belong to. Required. in: query name: project required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yTraceOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one trace's captured errors for a project \u2014 every error event that carried the trace id, in the\ \ order the events plane holds them." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/service/entry_point_operations: post: description: 'Returns one service''s entry-point operations with the same latency and error profile topOperations reports.' operationId: post_o11y_service_entry_point_operations requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yOperationsIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yOperationsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one service's entry-point operations with the same latency and error profile topOperations reports. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/service/top_level_operations: post: description: "Maps each service to its entry-point span names \u2014 for the\none service named in the request, or for\ \ every service when none is." operationId: post_o11y_service_top_level_operations requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yTopLevelOpsIn' required: true responses: '200': content: application/json: schema: additionalProperties: items: type: string type: array type: object description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Maps each service to its entry-point span names \u2014 for the one service named in the request, or for every\ \ service when none is." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/service/top_operations: post: description: 'Returns one service''s heaviest operations in the window, each with p50/p95/p99 latency, how often it ran and how often it errored.' operationId: post_o11y_service_top_operations requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yOperationsIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yOperationsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one service's heaviest operations in the window, each with p50/p95/p99 latency, how often it ran and how often it errored. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/service_accounts: get: description: Lists the caller's org's service accounts. operationId: ListServiceAccounts responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yServiceAccountsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller's org's service accounts. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: "Creates a service account in the caller's org,\nanswering its id. The name \u2014 a lowercase letter followed\ \ by lowercase\nletters, digits or hyphens \u2014 becomes the account's email local part." operationId: CreateServiceAccount requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yServiceAccountCreateIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yServiceAccountCreateOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a service account in the caller's org, answering its id. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/service_accounts/me: get: description: "Returns the calling service account itself, with the\nroles it holds \u2014 the self-inspection read for\ \ a key-authenticated caller." operationId: GetMyServiceAccount responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yServiceAccountOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the calling service account itself, with the roles it holds \u2014 the self-inspection read for a\ \ key-authenticated caller." tags: - o11y x-app: o11y x-public: true x-tool: true put: description: Renames the calling service account. operationId: UpdateMyServiceAccount requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yMyServiceAccountUpdateIn' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Renames the calling service account. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/service_accounts/{id}: delete: description: 'Deletes a service account and revokes every key it holds.' operationId: DeleteServiceAccount parameters: - in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes a service account and revokes every key it holds. tags: - o11y x-app: o11y x-public: true x-tool: true get: description: Returns one service account with the roles it holds. operationId: GetServiceAccount parameters: - in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yServiceAccountOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one service account with the roles it holds. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: Renames a service account. operationId: UpdateServiceAccount parameters: - in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yServiceAccountUpdateIn' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Renames a service account. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/service_accounts/{id}/keys: get: description: "Lists a service account's API keys \u2014 metadata only,\nnever the secrets." operationId: ListServiceAccountKeys parameters: - in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAPIKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists a service account's API keys \u2014 metadata only, never the secrets." tags: - o11y x-app: o11y x-public: true x-tool: true post: description: "Mints an API key for a service account and answers\nthe key's id and its secret \u2014 the one time the\ \ secret is ever shown.\n\nExpiresAt is a unix timestamp in seconds; zero means the key never expires,\nand a timestamp\ \ in the past is refused." operationId: CreateServiceAccountKey parameters: - in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAPIKeyCreateIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAPIKeyCreateOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Mints an API key for a service account and answers the key's id and its secret \u2014 the one time the secret\ \ is ever shown." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/service_accounts/{id}/keys/{fid}: delete: description: 'Revokes an API key. Revocation is immediate and permanent.' operationId: RevokeServiceAccountKey parameters: - in: path name: id required: true schema: type: string - in: path name: fid required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Revokes an API key. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: Renames an API key or moves its expiry. operationId: UpdateServiceAccountKey parameters: - in: path name: id required: true schema: type: string - in: path name: fid required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yAPIKeyUpdateIn' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Renames an API key or moves its expiry. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/service_accounts/{id}/roles: get: description: Lists the roles a service account holds. operationId: GetServiceAccountRoles parameters: - in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yServiceAccountRolesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the roles a service account holds. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Assigns a role, named by its id, to a service account.' operationId: CreateServiceAccountRole parameters: - description: RoleID is the id of the role to assign. Required. in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yServiceAccountRoleGrantIn' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Assigns a role, named by its id, to a service account. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/service_accounts/{id}/roles/{rid}: delete: description: Removes a role from a service account. operationId: DeleteServiceAccountRole parameters: - in: path name: id required: true schema: type: string - in: path name: rid required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a role from a service account. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/services: post: description: 'Lists the instrumented services seen in the window, each with the request profile of its entry-point spans: p99 and average latency, call and error rates, and the entry-point operations the numbers were computed over.' operationId: post_o11y_services requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yServicesIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yServicesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Lists the instrumented services seen in the window, each with the request profile of its entry-point spans: p99 and average latency, call and error rates, and the entry-point operations the numbers were computed over.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/services/list: get: description: "Lists the name of every service the trace store holds, with no\nwindow applied \u2014 the complete catalog,\ \ for pickers and autocomplete." operationId: get_o11y_services_list responses: '200': content: application/json: schema: items: type: string type: array description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the name of every service the trace store holds, with no window applied \u2014 the complete catalog,\ \ for pickers and autocomplete." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/sessions: get: description: "Answers the caller org's LLM-observability sessions \u2014 traces grouped by session id on the gen_ai\ \ span plane \u2014 paged by limit and offset, in the runtime's own envelope, passed through unchanged.\n\nAn org-less\ \ caller is refused HERE, at the cloud boundary, before the request reaches the runtime, and the org the runtime then\ \ scopes on is that SAME validated tenant. The two cannot disagree: the tenant is minted from the principal's own\ \ claim at ingress and a client copy never survives it.\n\nThere is deliberately no session-detail route to pair with\ \ this. The runtime serves the list only; detail is composed client-side from this list plus the traces filtered by\ \ session, so a caller looking for one is looking for something that was never served rather than something that broke." operationId: get_o11y_sessions summary: List the caller org's LLM sessions tags: - o11y x-app: o11y x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/o11y/settings/apdex: get: description: 'Returns apdex settings for the named services. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_settings_apdex parameters: - description: Services are the service names, comma separated. in: query name: services required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yApdexOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns apdex settings for the named services. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Sets one service''s apdex threshold and the status codes excluded from its score. Admin only, as the mux tree has always gated it (AdminAccess); the runtime''s own gate enforces it.' operationId: post_o11y_settings_apdex requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yApdexSetIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yApdexSetOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Sets one service's apdex threshold and the status codes excluded from its score. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/settings/ttl: get: description: 'Returns the org''s current retention policy: default TTL, custom per-label rules, and cold-storage settings where configured. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_settings_ttl responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRetentionOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the org''s current retention policy: default TTL, custom per-label rules, and cold-storage settings where configured.' tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Sets the org''s retention policy for one signal: the default TTL in days, ordered per-label retention rules, and optional cold-storage settings. Admin only, as the mux tree has always gated it (AdminAccess); the runtime''s own gate enforces it.' operationId: post_o11y_settings_ttl requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRetentionSetIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yRetentionSetOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Sets the org''s retention policy for one signal: the default TTL in days, ordered per-label retention rules, and optional cold-storage settings.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/span_mapper_groups: get: description: 'Lists the caller''s org''s mapping groups, optionally only the enabled ones. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: ListSpanMapperGroups parameters: - in: query name: enabled required: false schema: type: boolean responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySpanMapperGroupsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller's org's mapping groups, optionally only the enabled ones. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Creates a mapping group: the name it is known by, the span and resource attributes whose presence selects a span into it, and whether it is on. Callers need the admin role; the runtime''s own gate enforces it.' operationId: CreateSpanMapperGroup requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostableSpanMapperGroup' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySpanMapperGroupOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Creates a mapping group: the name it is known by, the span and resource attributes whose presence selects a span into it, and whether it is on.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/span_mapper_groups/{groupId}: delete: description: 'Deletes a mapping group and every mapper under it. Callers need the admin role; the runtime''s own gate enforces it.' operationId: DeleteSpanMapperGroup parameters: - in: path name: groupId required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes a mapping group and every mapper under it. tags: - o11y x-app: o11y x-public: true x-tool: true patch: description: 'Changes a group''s name, condition or enabled state. Every field is optional and only the ones sent are applied. Callers need the admin role; the runtime''s own gate enforces it.' operationId: UpdateSpanMapperGroup parameters: - in: path name: groupId required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySpanMapperGroupUpdateIn' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes a group's name, condition or enabled state. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/span_mapper_groups/{groupId}/span_mappers: get: description: 'Lists the mappers belonging to one group, in the order they are applied. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: ListSpanMappers parameters: - in: path name: groupId required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySpanMappersOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the mappers belonging to one group, in the order they are applied. tags: - o11y x-app: o11y x-public: true x-tool: true post: description: 'Adds a mapper to a group: which field context it reads, the move or copy it performs, and whether it is on. Callers need the admin role; the runtime''s own gate enforces it.' operationId: CreateSpanMapper parameters: - in: path name: groupId required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySpanMapperCreateIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySpanMapperOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Adds a mapper to a group: which field context it reads, the move or copy it performs, and whether it is on.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/span_mapper_groups/{groupId}/span_mappers/{mapperId}: delete: description: 'Deletes one mapper from a group. Callers need the admin role; the runtime''s own gate enforces it.' operationId: DeleteSpanMapper parameters: - in: path name: groupId required: true schema: type: string - in: path name: mapperId required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes one mapper from a group. tags: - o11y x-app: o11y x-public: true x-tool: true patch: description: 'Changes a mapper''s field context, config or enabled state. Every field is optional and only the ones sent are applied. Callers need the admin role; the runtime''s own gate enforces it.' operationId: UpdateSpanMapper parameters: - in: path name: groupId required: true schema: type: string - in: path name: mapperId required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySpanMapperUpdateIn' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes a mapper's field context, config or enabled state. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/span_percentile: post: description: 'Places one span''s duration among its peers: the p50/p90/p99 durations of like spans, and the percentile the given duration lands at. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: post_o11y_span_percentile requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySpanPercentileIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySpanPercentileOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Places one span''s duration among its peers: the p50/p90/p99 durations of like spans, and the percentile the given duration lands at.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/statefulsets/attribute_keys: get: description: 'Lists the metric attribute keys Kubernetes statefulsets report, for building statefulset filters.' operationId: get_o11y_statefulsets_attribute_keys parameters: - description: "DataSource is the telemetry the keys come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the keys will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the keys must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: SearchText narrows the keys to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the keys to one kind \u2014 tag or resource. Empty means all;\nan invalid value reads\ \ as empty." in: query name: tagType required: false schema: type: string - description: Limit caps how many keys come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeKeysOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the metric attribute keys Kubernetes statefulsets report, for building statefulset filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/statefulsets/attribute_values: get: description: 'Lists the values one statefulset attribute key has taken, for building statefulset filters.' operationId: get_o11y_statefulsets_attribute_values parameters: - description: "DataSource is the telemetry the values come from \u2014 metrics for the infra\nfaces. The runtime requires\ \ it." in: query name: dataSource required: false schema: type: string - description: 'AggregateOperator is the aggregation the values will be used under, e.g. noop, count, avg. The runtime requires it for non-metrics sources.' in: query name: aggregateOperator required: false schema: type: string - description: AggregateAttribute is the metric the values must appear on. in: query name: aggregateAttribute required: false schema: type: string - description: AttributeKey is the key whose values to list. in: query name: attributeKey required: false schema: type: string - description: "FilterAttributeKeyDataType is the key's data type \u2014 string, int64,\nfloat64 or bool. Empty means\ \ unspecified." in: query name: filterAttributeKeyDataType required: false schema: type: string - description: SearchText narrows the values to those containing it. in: query name: searchText required: false schema: type: string - description: "TagType narrows the search to one kind of key \u2014 tag or resource." in: query name: tagType required: false schema: type: string - description: Limit caps how many values come back. Absent means 50. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yInfraAttributeValuesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the values one statefulset attribute key has taken, for building statefulset filters. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/statefulsets/list: post: description: 'Lists Kubernetes statefulsets over a time range, each with the CPU and memory its pods used against request and limit, desired and available replica counts, restarts and attributes; filterable, groupable and paginated.' operationId: post_o11y_statefulsets_list requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.StatefulSetListRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yStatefulSetListOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Kubernetes statefulsets over a time range, each with the CPU and memory its pods used against request and limit, desired and available replica counts, restarts and attributes; filterable, groupable and paginated. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/stats: get: description: "Returns the collected usage statistics for the caller's org, as the\nstats reporter aggregates them \u2014\ \ a map whose keys are the reporter's own\ncounter names.\n\nCallers need the viewer role; the runtime's own gate\ \ enforces it." operationId: get_o11y_stats responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yOrgStatsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the collected usage statistics for the caller's org, as the stats reporter aggregates them \u2014\ \ a map whose keys are the reporter's own counter names." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/status: get: description: "Reports whether a product's service is live: an in-cluster\nhealth probe with its measured latency, fused\ \ with the per-replica up\ninventory. Infra health is not tenant-partitioned \u2014 a service is up or down\nfor everyone\ \ \u2014 so any validated caller is served, but an unvalidated one is\nrefused. A product with no backing workload\ \ answers down/unknown-service\nwithout probing anything; a malformed slug is a 400." operationId: get_o11y_status parameters: - description: Product is the console product slug to probe, e.g. "kms". Required. example: kms in: query name: product required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.statusResult' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Reports whether a product''s service is live: an in-cluster health probe with its measured latency, fused with the per-replica up inventory.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/substitute_vars: post: description: "Substitutes a query's variables and returns the\nresolved request, without running it \u2014 what a dashboard\ \ does before it\nqueries.\n\nCallers need the viewer role; the runtime's own gate enforces it." operationId: post_o11y_substitute_vars requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.QueryRangeRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11ySubstituteVarsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Substitutes a query's variables and returns the resolved request, without running it \u2014 what a dashboard\ \ does before it queries." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/summary: get: description: "Reports whether the platform is up. It returns the public status\ndocument: the incidents currently open\ \ against Hanzo's own services, derived\nfrom the fleet health probes, plus the address of the human status page.\ \ No\nauthentication is required and no tenant data is involved \u2014 the answer is the\nsame for every caller.\n\ \nA service that fails its health probe becomes one incident naming that service.\nWhen the availability source itself\ \ cannot be read the endpoint answers 503\nrather than an empty incident list, because \"we cannot tell\" and \"everything\ \ is\nfine\" are different answers and only one of them is true." operationId: get_o11y_summary responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.StatusSummary' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports whether the platform is up. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/testChannel: post: description: 'Sends a test notification to the posted receiver. The legacy path; prefer /channels/test. Editor gate.' operationId: TestChannelDeprecated requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.alertmanagertypes.Receiver' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Sends a test notification to the posted receiver. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/testRule: post: description: 'Fires a test notification for the posted rule definition and answers with how many series alerted and a status message. The legacy path; prefer /rules/test. Editor gate.' operationId: TestRuleNotification requestBody: content: application/json: schema: {} required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yTestNotificationOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Fires a test notification for the posted rule definition and answers with how many series alerted and a status message. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/third-party-apis/overview/domain: post: description: "Returns one external domain's endpoint-level breakdown \u2014 each\nendpoint with its rate, error and\ \ latency columns over the window." operationId: post_o11y_third-party-apis_overview_domain requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDomainsIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDomainsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one external domain's endpoint-level breakdown \u2014 each endpoint with its rate, error and latency\ \ columns over the window." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/third-party-apis/overview/list: post: description: 'Lists the external domains the instrumented services call, with request rate, error percentage and latency per domain. Rows whose domain is a bare IP address are dropped unless show_ip asks for them.' operationId: post_o11y_third-party-apis_overview_list requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDomainsIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDomainsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the external domains the instrumented services call, with request rate, error percentage and latency per domain. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/trace-funnels/analytics/error-traces: post: description: 'Returns the errored traces through a step transition of a funnel described inline.' operationId: GetDraftFunnelErrorTraces requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDraftFunnelIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelRowsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the errored traces through a step transition of a funnel described inline. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/trace-funnels/analytics/overview: post: description: 'Returns the conversion overview of a funnel described inline.' operationId: GetDraftFunnelOverview requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDraftFunnelIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelRowsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the conversion overview of a funnel described inline. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/trace-funnels/analytics/slow-traces: post: description: 'Returns the slowest traces through a step transition of a funnel described inline.' operationId: GetDraftFunnelSlowTraces requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDraftFunnelIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelRowsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the slowest traces through a step transition of a funnel described inline. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/trace-funnels/analytics/steps: post: description: Returns the per-step metrics of a funnel described inline. operationId: GetDraftFunnelStepMetrics requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDraftFunnelIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelRowsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the per-step metrics of a funnel described inline. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/trace-funnels/analytics/steps/overview: post: description: 'Returns the conversion between two steps of a funnel described inline.' operationId: GetDraftFunnelStepOverview requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDraftFunnelIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelRowsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the conversion between two steps of a funnel described inline. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/trace-funnels/analytics/validate: post: description: "Lists the traces that match a funnel described inline \u2014\nthe builder's \"try this\" before anything\ \ is saved." operationId: ValidateDraftFunnelTraces requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDraftFunnelIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelRowsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the traces that match a funnel described inline \u2014 the builder's \"try this\" before anything is\ \ saved." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/trace-funnels/list: get: description: 'Lists the caller''s org''s funnels, each with its steps and who last touched it. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: ListTraceFunnels responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller's org's funnels, each with its steps and who last touched it. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/trace-funnels/new: post: description: 'Creates an empty funnel with a name, answering the funnel it created. Steps are added afterwards with the steps update. Callers need the editor role; the runtime''s own gate enforces it.' operationId: CreateTraceFunnel requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelCreateIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates an empty funnel with a name, answering the funnel it created. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/trace-funnels/steps/update: put: description: "Replaces a funnel's steps \u2014 the funnel is named in the body\nrather than the path \u2014 and answers\ \ the funnel as it now stands. A name or\ndescription sent alongside is applied too; an empty one leaves it as it\ \ was.\n\nCallers need the editor role; the runtime's own gate enforces it." operationId: UpdateTraceFunnelSteps requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelStepsUpdateIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Replaces a funnel's steps \u2014 the funnel is named in the body rather than the path \u2014 and answers the\ \ funnel as it now stands." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/trace-funnels/{funnel_id}: delete: description: "Deletes a funnel. The answer carries no data \u2014 the runtime\nacknowledges with the success envelope\ \ alone, which is what this Out says.\n\nCallers need the editor role; the runtime's own gate enforces it." operationId: DeleteTraceFunnel parameters: - in: path name: funnel_id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelDeleteOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes a funnel. tags: - o11y x-app: o11y x-public: true x-tool: true get: description: 'Returns one funnel with its steps. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: GetTraceFunnel parameters: - in: path name: funnel_id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one funnel with its steps. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: 'Renames a funnel or rewrites its description, answering the funnel as it now stands. Callers need the editor role; the runtime''s own gate enforces it.' operationId: UpdateTraceFunnel parameters: - in: path name: funnel_id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelUpdateIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Renames a funnel or rewrites its description, answering the funnel as it now stands. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/trace-funnels/{funnel_id}/analytics/error-traces: post: description: "Returns the errored traces through a step transition of a\nsaved funnel \u2014 the entry point for \"\ why is this step failing\"." operationId: GetTraceFunnelErrorTraces parameters: - in: path name: funnel_id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelStepWindowIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelRowsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the errored traces through a step transition of a saved funnel \u2014 the entry point for \"why is\ \ this step failing\"." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/trace-funnels/{funnel_id}/analytics/overview: post: description: 'Returns a saved funnel''s conversion overview over a window: how many entered, how many converted, the rate and the latency.' operationId: GetTraceFunnelOverview parameters: - in: path name: funnel_id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelStepWindowIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelRowsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns a saved funnel''s conversion overview over a window: how many entered, how many converted, the rate and the latency.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/trace-funnels/{funnel_id}/analytics/slow-traces: post: description: "Returns the slowest traces through a step transition of a\nsaved funnel \u2014 the entry point for \"\ why is this step slow\"." operationId: GetTraceFunnelSlowTraces parameters: - in: path name: funnel_id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelStepWindowIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelRowsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the slowest traces through a step transition of a saved funnel \u2014 the entry point for \"why is\ \ this step slow\"." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/trace-funnels/{funnel_id}/analytics/steps: post: description: "Returns a saved funnel's per-step metrics over a window \u2014 the\ncounts and latencies at each step,\ \ in step order." operationId: GetTraceFunnelStepMetrics parameters: - in: path name: funnel_id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelWindowIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelRowsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns a saved funnel's per-step metrics over a window \u2014 the counts and latencies at each step, in step\ \ order." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/trace-funnels/{funnel_id}/analytics/steps/overview: post: description: "Returns the conversion between two named steps of a saved\nfunnel \u2014 the step-to-step drill-down behind\ \ the overview." operationId: GetTraceFunnelStepOverview parameters: - in: path name: funnel_id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelStepWindowIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelRowsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the conversion between two named steps of a saved funnel \u2014 the step-to-step drill-down behind\ \ the overview." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/trace-funnels/{funnel_id}/analytics/validate: post: description: "Lists the traces that match a saved funnel over a window \u2014 the\nread that answers \"is this funnel\ \ finding anything at all\"." operationId: ValidateTraceFunnelTraces parameters: - in: path name: funnel_id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelWindowIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFunnelRowsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the traces that match a saved funnel over a window \u2014 the read that answers \"is this funnel finding\ \ anything at all\"." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/traces: get: description: "Lists the caller org's recent traces \u2014 one row per trace with\nits span count and wall-clock duration,\ \ most recently active first. This is\nthe trace SEARCH: it is where a trace id comes from, and the spans behind any\n\ row are then read from GET /v1/o11y/traces/{traceId}. Every row belongs to the\ncaller's own org \u2014 the tenant\ \ is the validated principal, never an input, and\nthere is no administrator widening, because a trace list is a tenant's\ \ records\nrather than a rollup over them. An unreachable telemetry store answers 503\nrather than an empty page,\ \ because \"no traces\" and \"cannot see the traces\" are\ndifferent facts and only one of them is about the caller's\ \ system." operationId: get_o11y_traces parameters: - description: 'Range is the window in seconds, counted back from now over each trace''s last activity. Default 3600, capped at 604800 (7d).' example: 3600 in: query name: range required: false schema: format: int64 type: integer - description: Limit is how many traces to return. Default 50, capped at 500. example: 50 in: query name: limit required: false schema: format: int64 type: integer - description: 'MinDurationMs keeps only traces that lasted at least this many milliseconds. Zero or absent keeps every trace in the window.' in: query name: minDurationMs required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.tracesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the caller org's recent traces \u2014 one row per trace with its span count and wall-clock duration,\ \ most recently active first." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/traces/fields: get: description: 'Returns the trace field catalog: the span fields already selected as indexed columns, and the interesting ones seen in the data that could be. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: GetTraceFields responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFieldCatalogOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the trace field catalog: the span fields already selected as indexed columns, and the interesting ones seen in the data that could be.' tags: - o11y x-app: o11y x-public: true x-tool: true post: description: "Changes how one span field is stored \u2014 selects or deselects\nit as a materialized column and tunes\ \ its index \u2014 and echoes the setting back.\n\nCallers need the editor role; the runtime's own gate enforces it." operationId: UpdateTraceField requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFieldSetting' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yFieldSetting' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Changes how one span field is stored \u2014 selects or deselects it as a materialized column and tunes its\ \ index \u2014 and echoes the setting back." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/traces/{traceId}: get: description: "Returns one trace's spans as a column/row table, optionally\ncentred on a span and walked a fixed number\ \ of levels up and down from it \u2014\nthe read the trace explorer opens a trace with.\n\nCallers need the viewer\ \ role; the runtime's own gate enforces it." operationId: SearchTraces parameters: - in: path name: traceId required: true schema: type: string - in: query name: spanId required: false schema: type: string - in: query name: levelUp required: false schema: format: int64 type: integer - in: query name: levelDown required: false schema: format: int64 type: integer - in: query name: spanRenderLimit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/o11y.O11yTraceSpanWindow' type: array description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one trace's spans as a column/row table, optionally centred on a span and walked a fixed number of\ \ levels up and down from it \u2014 the read the trace explorer opens a trace with." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/traces/{traceId}/aggregations: post: description: "Computes span aggregations over one trace \u2014 span count,\nduration or share of execution time \u2014\ \ grouped by the resource field each\naggregation names.\n\nCallers need the viewer role; the runtime's own gate enforces\ \ it." operationId: GetTraceAggregations parameters: - in: path name: traceId required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yTraceAggregationsIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yTraceAggregationsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Computes span aggregations over one trace \u2014 span count, duration or share of execution time \u2014 grouped\ \ by the resource field each aggregation names." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/traces/{traceId}/flamegraph: post: description: 'Returns a trace''s flamegraph: spans bucketed by depth level, each level ordered as it is drawn, around the selected span. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: GetFlamegraph parameters: - in: path name: traceId required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yTraceFlamegraphIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yTraceFlamegraphOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns a trace''s flamegraph: spans bucketed by depth level, each level ordered as it is drawn, around the selected span.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/traces/{traceId}/waterfall: post: description: 'Returns a trace''s waterfall: every span when the trace is small enough, a capped window around the selected span when it is not, with the uncollapsed subtrees the caller asked to keep open. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: GetWaterfallV4 parameters: - in: path name: traceId required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yTraceWaterfallIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yTraceWaterfallOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns a trace''s waterfall: every span when the trace is small enough, a capped window around the selected span when it is not, with the uncollapsed subtrees the caller asked to keep open.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/usage: get: description: 'Returns ingestion usage counts bucketed over the requested window, optionally narrowed to one service. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: get_o11y_usage parameters: - description: Start is the window start, as epoch nanoseconds. Required. in: query name: start required: true schema: type: string - description: End is the window end, as epoch nanoseconds. Required. in: query name: end required: true schema: type: string - description: Step is the bucket width in seconds. The runtime requires it. in: query name: step required: false schema: format: int64 type: integer - description: Service narrows usage to one service. Empty covers all. in: query name: service required: false schema: type: string responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/o11y.O11yUsageItem' type: array description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns ingestion usage counts bucketed over the requested window, optionally narrowed to one service. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/user/preferences: get: description: 'Lists every preference of the calling user, each with its current and default value. Viewer gate.' operationId: ListUserPreferences responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yPreferencesOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists every preference of the calling user, each with its current and default value. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/user/preferences/{name}: get: description: 'Returns one preference of the calling user, by name. Viewer gate.' operationId: GetUserPreference parameters: - in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yPreferenceOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one preference of the calling user, by name. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: 'Sets one preference of the calling user, by name. Viewer gate.' operationId: UpdateUserPreference parameters: - in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yUpdatablePreference' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Sets one preference of the calling user, by name. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/users/me: get: description: 'Returns the calling user together with every role they hold. Open to any authenticated caller.' operationId: GetMyUser responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yUserOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the calling user together with every role they hold. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/users/me/dashboards: get: description: 'Is dashboardListV2 personalized for the calling user: each dashboard carries the caller''s pinned state, and pinned dashboards float to the top of the requested ordering. Supports the same filter DSL, sort, order and pagination. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: ListDashboardsForUserV2 parameters: - description: 'Query is the filter DSL over dashboard columns and tags, e.g. `name:cpu source:user`. Empty lists everything.' in: query name: query required: false schema: type: string - description: 'Sort is the sort field: updated_at, created_at or name. Empty sorts by updated_at.' in: query name: sort required: false schema: type: string - description: 'Order is the sort direction: asc or desc. Empty orders desc.' in: query name: order required: false schema: type: string - description: 'Limit caps how many dashboards come back. Zero means the default of 20; the runtime caps it at 200.' in: query name: limit required: false schema: format: int64 type: integer - description: Offset is how many dashboards to skip for pagination. in: query name: offset required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardListForUserOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Is dashboardListV2 personalized for the calling user: each dashboard carries the caller''s pinned state, and pinned dashboards float to the top of the requested ordering.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/users/me/dashboards/{id}/pins: delete: description: "Removes the caller's pin for a dashboard. Idempotent \u2014\nunpinning a dashboard that was not pinned\ \ still succeeds.\n\nCallers need the viewer role; the runtime's own gate enforces it." operationId: UnpinDashboardV2 parameters: - description: ID is the resource id from the path. in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes the caller's pin for a dashboard. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: 'Pins a dashboard for the calling user. A user can pin at most ten dashboards; pinning at the limit refuses with the runtime''s conflict. Re-pinning an already-pinned dashboard is a no-op success. Pinning mutates only the caller''s pin list, not the dashboard, so a viewer may pin what a viewer may read. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: PinDashboardV2 parameters: - description: ID is the resource id from the path. in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Pins a dashboard for the calling user. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/variables/query: post: description: 'Evaluates a dashboard variable query and returns the values the variable may take. Callers need the viewer role; the runtime''s own gate enforces it.' operationId: post_o11y_variables_query requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardVarsIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yDashboardVarsOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Evaluates a dashboard variable query and returns the values the variable may take. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/version: get: description: 'Reports the running build: its version, whether an enterprise edition is present ("N" in this build), and whether first-user setup has completed. Open by design; the runtime''s own gate is OpenAccess.' operationId: get_o11y_version responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yVersionOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Reports the running build: its version, whether an enterprise edition is present ("N" in this build), and whether first-user setup has completed.' tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/zeus/hosts: get: description: Returns the deployment's host info from Zeus. Viewer gate. operationId: GetHosts responses: '200': content: application/json: schema: $ref: '#/components/schemas/o11y.O11yGettableHostOut' description: ok '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the deployment's host info from Zeus. tags: - o11y x-app: o11y x-public: true x-tool: true put: description: 'Records the deployment''s host in Zeus, overwriting any prior one. Admin gate.' operationId: PutHost requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostableHost' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records the deployment's host in Zeus, overwriting any prior one. tags: - o11y x-app: o11y x-public: true x-tool: true /v1/o11y/zeus/profiles: put: description: "Records the deployment's profile in Zeus \u2014 how the team uses\nobservability today and what they plan\ \ \u2014 overwriting any prior one. Admin\ngate." operationId: PutProfile requestBody: content: application/json: schema: $ref: '#/components/schemas/o11y.PostableProfile' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/Approval' description: held for approval '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Records the deployment's profile in Zeus \u2014 how the team uses observability today and what they plan \u2014\ \ overwriting any prior one." tags: - o11y x-app: o11y x-public: true x-tool: true /v1/openapi.json: get: description: "Serves the OpenAPI document for the routes this process actually answers \u2014 generated from the live\ \ router at request time, not from a checked-in file that can disagree with it.\n\nOn an app it is that app's own\ \ surface; on the fleet's public endpoint it is the composed document for every mounted app. Unauthenticated by design:\ \ a client has to be able to read the contract before it holds a credential, and the document grants nothing.\n\n\ Rendered once and served as bytes thereafter, so the route table's immutability is what makes a repeat request a memcpy\ \ rather than a re-encode of a megabyte document." operationId: get_openapi.json security: [] summary: The API description this SDK was generated from tags: - openapi x-app: openapi x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/openapi/commands: get: description: "The command projection of the OpenAPI document at /v1/openapi.json \u2014 each operation reduced to what\ \ running it by name needs: its service and command token, its method and path, the prose lifted from the handler,\ \ its path parameters as positional arguments and its remaining inputs as typed flags.\n\nIt is a separate address\ \ for one measured reason: the fleet document is megabytes and a command palette cannot load it, while this projection\ \ of the same operations is several times smaller because it carries no schemas, responses or components.\n\nUnauthenticated\ \ by design, exactly as the document it derives from: a client has to be able to read the contract before it holds\ \ a credential, and a list of operation names grants nothing. The list is TOTAL and is never filtered by caller \u2014\ \ what you may run is decided per request by the authorizer, on the decoded input, so a filtered list would be a second\ \ claim about permission that is free to be wrong.\n\nRendered once and served as bytes thereafter, under a strong\ \ ETag." operationId: get_openapi_commands security: [] summary: Every operation this API answers, as a command tags: - openapi x-app: openapi x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/patrol/alarm: post: description: "Takes one activation. It records the activation whatever happens, then\nmatches a site \u2014 the one\ \ the caller named, else the one whose account number\nreported \u2014 and on a match opens an incident in state received\ \ with that site's\ntier and SLA, puts the site into alarm, raises a red feed row and tells the\ncontrollers. An activation\ \ that matches no site is still recorded, raises an\namber row, and answers 202: an activation is never dropped.\n\ \nIt admits the receiving centre's own credential and a controller keying one in\nby hand. The centre holds the `centre`\ \ role its tenant granted it, which posts\nan activation and reads nothing, like every other identity here.\n\nThe\ \ centre's own reference identifies the activation while the response it\nopened is still running, so a redelivery\ \ is answered with that response rather\nthan dispatching a second unit. Centres recycle references, so once the\n\ response has closed the same one is the next activation." operationId: post_patrol_alarm requestBody: content: application/json: schema: $ref: '#/components/schemas/patrol.patrolAlarmIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolAlarmOut' description: created '202': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolAlarmOut' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Takes one activation. tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/camera: get: description: "Lists the cameras a caller may OPEN. A row somebody can never reach is\na catalogue of a customer's premises\ \ and nothing else, so the admission the\nstream enforces is asked here too rather than left to differ from it.\n\n\ The stream credential is not part of a camera row \u2014 it is a KMS path the\ndocument store never holds \u2014 so\ \ what comes back says where a camera is and\nnever how to reach it." operationId: get_patrol_camera parameters: - description: 'Site narrows the list to one site by document name. Empty lists every camera.' in: query name: site required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolCameraList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the cameras a caller may OPEN. tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/camera/{name}/stream: get: description: "Mints a short-lived way in to one camera.\n\nThe credential is kept in KMS, at the one place a camera's\ \ credential is kept\n\u2014 the org's own patrol namespace, named for the camera \u2014 and what comes back\nis minted\ \ from it rather than being it: the camera's address with the user and\npassword removed, bounded by an expiry, and\ \ signed so that whatever opens the\ncamera can tell a live ticket from a made-up one. The caller receives\nsomething\ \ that opens one camera for five minutes and says nothing about how.\n\nA camera whose row points anywhere but its\ \ own place, and one with no\ncredential registered, is 404." operationId: get_patrol_camera_by_name_stream parameters: - description: Name is the camera's document name, from the path. in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolStreamOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Mints a short-lived way in to one camera. tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/camera/{name}/stream/{ticket}: get: description: "Redeems a minted ticket for what opens the camera.\n\nThe mint is the operation's \u2014 staff only \u2014\ \ and it hands back the camera's\naddress with the credential stripped out of it. This is where the holder of\nthat\ \ address gets what actually dials the camera, and it asks for no\ncredential of its own: whatever opens a camera\ \ is a relay, not a member of the\noperation, and a check only the issuer could perform checks nothing.\n\nThe ticket\ \ IS the authority. It names one camera, it covers one moment, and it\nis tagged with a key nobody but this deployment\ \ holds, so holding a live one is\nthe right to open that camera until it runs out \u2014 which is the whole of what\n\ was issued, and the reason issuing one is staff-only and five minutes long.\n\nA tag that does not verify is 404 \u2014\ \ the route is no oracle on which tenants or\ncameras exist, or which tickets were ever issued \u2014 and one that\ \ verifies but\nhas passed its moment is 410, because that ticket was real and is over." operationId: get_patrol_camera_by_name_stream_by_ticket parameters: - description: Name is the camera's document name, from the path. in: path name: name required: true schema: type: string - description: Ticket is the tag the minted address carries, from the path. in: path name: ticket required: true schema: type: string - description: 'Expires is the moment the tag covers, as the seconds the minted address carries in its `expires` parameter.' in: query name: expires required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolTicketOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Redeems a minted ticket for what opens the camera. tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/event: get: description: "Lists the feed, newest first. It is the same rows the live stream\ncarries, read back for a screen that\ \ has just opened \u2014 so the window is the\nnewest rows, or a screen that reloads reads a feed that stopped on\ \ the day the\ntenant was installed." operationId: get_patrol_event parameters: - description: 'Level narrows the list to red, amber, green or cyan. Empty lists every level.' in: query name: level required: false schema: type: string - description: 'Limit caps how many rows are returned. Anything that is not a positive integer uses 80, and higher values are clamped to 80.' in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolEventList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the feed, newest first. tags: - patrol x-app: patrol x-public: true x-tool: true post: description: 'Writes one feed row by hand and pushes it to every screen watching. It is how a controller puts something on the record that no automatic write would have produced.' operationId: post_patrol_event requestBody: content: application/json: schema: $ref: '#/components/schemas/patrol.patrolEventIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolEventOut' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Writes one feed row by hand and pushes it to every screen watching. tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/feed: get: description: 'Holds a Server-Sent Events connection open and writes every change in the picture as it happens: a unit''s position, an incident moving, a feed row, a checkpoint confirmed. Each record is one `data:` line carrying a JSON frame whose `kind` says which of the four it is, terminated by a blank line; a comment line arrives every 25 seconds so an idle connection stays open. It carries the caller''s own org and no other. ?since= replays the feed rows written at or after that moment before the stream goes live, so a reader that reconnected misses nothing it can still see. The list routes are the truth and the stream is a live hint: a dropped frame costs one refresh.' operationId: get_patrol_feed summary: Watch the live feed tags: - patrol x-app: patrol x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/patrol/incident: get: description: Lists responses. ?open=1 narrows it to the ones still running. operationId: get_patrol_incident parameters: - description: Open lists only the incidents that are not closed when it is 1. in: query name: open required: false schema: format: int64 type: integer - description: 'Limit caps how many are returned. Anything that is not a positive integer uses 50, and higher values are clamped to 50.' in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolIncidentList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists responses. tags: - patrol x-app: patrol x-public: true x-tool: true post: description: 'Records a response by hand, in state received and carrying the site''s tier and SLA. It is what a controller calls when an activation arrives by phone rather than over the intake.' operationId: post_patrol_incident requestBody: content: application/json: schema: $ref: '#/components/schemas/patrol.patrolIncidentIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolIncidentOut' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records a response by hand, in state received and carrying the site's tier and SLA. tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/incident/{name}: get: description: 'Returns one response whole: the incident, every act performed on it, and the SLA clocks those acts settled. The clocks are derived from the stamps and stored nowhere.' operationId: get_patrol_incident_by_name parameters: - description: Name is the incident's document name, from the path. in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolIncidentOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one response whole: the incident, every act performed on it, and the SLA clocks those acts settled.' tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/incident/{name}/step: post: description: "Performs one act on an incident: it stamps the act's time, moves\nthe state, appends the act to the trail,\ \ and runs what the act sets in motion \u2014\na unit assigned, a site secured, a report filed, a controller told.\n\ \nThe states run forward only. An act from a state it does not leave is 409\nnaming the state the incident is in,\ \ and a caller without the act's roles is\nrefused." operationId: post_patrol_incident_by_name_step parameters: - description: Name is the incident's document name, from the path. in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/patrol.patrolIncidentAct' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolIncidentOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Performs one act on an incident: it stamps the act's time, moves the state, appends the act to the trail,\ \ and runs what the act sets in motion \u2014 a unit assigned, a site secured, a report filed, a controller told." tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/key: get: description: 'Lists the key register. Neither the officer nor the client reaches it: patrol.key is its own DocType and its permissions name neither, so there is no key row for either to read.' operationId: get_patrol_key responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolKeyList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the key register. tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/key/{name}: get: description: Returns one key set from the register. operationId: get_patrol_key_by_name parameters: - description: Name is the key set's reference, from the path. in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolKeyOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one key set from the register. tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/key/{name}/move: post: description: 'Records a key set leaving, returning, or being counted, and moves the register row to match. The register says where a set is now and the movements say how it got there, so a movement is written once and never edited. Who signed for the move is the caller, stamped from the request. A body that named the signer would make the chain of custody a value the signer chose.' operationId: post_patrol_key_by_name_move parameters: - description: Name is the key set's reference, from the path. in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/patrol.patrolMoveIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolKeyOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records a key set leaving, returning, or being counted, and moves the register row to match. tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/note: get: description: 'Lists internal remarks. The client never reaches them: patrol.note is its own DocType and its permissions do not name the client, so there is no note row for a customer to read.' operationId: get_patrol_note parameters: - description: 'Subject narrows the list to one subject by exact match. Empty lists every note.' in: query name: subject required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolNoteList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists internal remarks. tags: - patrol x-app: patrol x-public: true x-tool: true post: description: Records an internal remark, stamped with who wrote it. operationId: post_patrol_note requestBody: content: application/json: schema: $ref: '#/components/schemas/patrol.patrolNoteIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolNoteOut' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records an internal remark, stamped with who wrote it. tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/point/{name}/confirm: post: description: "Records presence at a checkpoint: who confirmed it, the method, where\nthey stood, and the photo key if\ \ one was taken. A confirmation after the\nwindow shuts is recorded late rather than refused \u2014 the visit happened,\ \ and a\nreport that hides it is worth nothing.\n\nAn officer confirms their own round and no other, by the same rule\ \ a position\nreport and every incident act are held to: the checkpoint belongs to a tour,\nthe tour names a unit,\ \ and the unit names its officer. The record carries the\nactor, so it says who claimed the visit and not merely that\ \ somebody did." operationId: post_patrol_point_by_name_confirm parameters: - description: Name is the checkpoint's document name, from the path. in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/patrol.patrolConfirmIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolCheckpointOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Records presence at a checkpoint: who confirmed it, the method, where they stood, and the photo key if one was taken.' tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/report: post: description: 'Composes and files a report from one estate read, so the same figures read the same a month later. The text is stored on the document; the PDF is produced on read at /v1/patrol/report/{name}. A client report naming a customer is an account of that customer''s estate and is theirs to download; naming none covers every customer, which is an account of the operation.' operationId: post_patrol_report requestBody: content: application/json: schema: $ref: '#/components/schemas/patrol.patrolReportIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolReportOut' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Composes and files a report from one estate read, so the same figures read the same a month later. tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/report/{name}: get: description: 'Renders a filed report as a PDF and answers with the bytes under an attachment filename. ?format=text answers the stored text verbatim under text/plain, which is what a copy button reads. Everyone but the customer may read every report; a client reads only a report about a site of their own, and anything else is 404 rather than a refusal, so the route is no existence oracle. A report whose text cannot be set answers that text rather than failing, because the account is the thing being asked for.' operationId: get_patrol_report_by_name parameters: - in: path name: name required: true schema: type: string summary: Download a filed report tags: - patrol x-app: patrol x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/patrol/site: get: description: 'Lists the estate. A client sees only the sites their own customer record owns, filtered from their role rather than from anything they sent.' operationId: get_patrol_site parameters: - description: 'Limit caps how many sites are returned. Anything that is not a positive integer uses 500, and higher values are clamped to 500.' in: query name: limit required: false schema: format: int64 type: integer - description: 'Status narrows the list to ok, fault, alarm or offline. Empty lists every status.' in: query name: status required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolSiteList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the estate. tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/site/{name}: get: description: 'Returns one site whole: the register row plus the panel''s zones and the standing hazards, which ride inside the document. A client asking for a site that is not theirs is 404.' operationId: get_patrol_site_by_name parameters: - description: Name is the site's document name, from the path. in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolSiteOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one site whole: the register row plus the panel''s zones and the standing hazards, which ride inside the document.' tags: - patrol x-app: patrol x-public: true x-tool: true put: description: "Changes a site's contract and brief fields. It leaves absent fields\nas they stand, and it never moves\ \ the account number, the coordinates or the\ncustomer \u2014 those are register facts, changed through the framework\ \ surface." operationId: put_patrol_site_by_name parameters: - description: Name is the site's document name, from the path. in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/patrol.patrolSiteEdit' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolSiteOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes a site's contract and brief fields. tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/snapshot: get: description: 'Returns the whole picture in one read: sites, units with their position trails, today''s tours and checkpoints, the key register, the recent feed and the open incident. It replaces the five separate list calls the screen used to make. A client sees only their own sites, and the key register is empty for anyone whose roles do not reach it.' operationId: get_patrol_snapshot parameters: - description: 'Events caps how many feed rows ride along. Anything that is not a positive integer uses 80, and higher values are clamped to 80.' in: query name: events required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolSnapshotOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the whole picture in one read: sites, units with their position trails, today''s tours and checkpoints, the key register, the recent feed and the open incident.' tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/tenant/{org}: get: description: "Returns one org's public face \u2014 brand, company, operations, features\nand terminology. It is the\ \ one anonymous route here: the front end loads it\nbefore anyone signs in. It carries no contact list and nothing\ \ operational,\nand an org with no tenant document is 404.\n\nThe org is a name a stranger supplied, so the read asks\ \ whether the deployment\nalready holds that tenant before it opens anything. Resolving a store CREATES\none, and\ \ a caller naming a fresh org on every request would leave a database\nper name they invented." operationId: get_patrol_tenant_by_org parameters: - description: Org is the IAM org the tenant belongs to, from the path. in: path name: org required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolTenantOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one org's public face \u2014 brand, company, operations, features and terminology." tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/tour: get: description: "Lists patrol rounds with their checkpoints \u2014 the proof-of-presence\nrecord. ?date=today narrows it\ \ to the current day." operationId: get_patrol_tour parameters: - description: 'Date narrows the list to one day, as YYYY-MM-DD. "today" means today. Empty lists every round held.' in: query name: date required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolTourList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists patrol rounds with their checkpoints \u2014 the proof-of-presence record." tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/unit: get: description: "Lists the fleet with each unit's position trail, which is what the map\ndraws. It is the operation's own:\ \ a unit names no site, so a customer reads\nnone of it \u2014 the officer crewing one is the company's staff, and\ \ the trail is\nwhere that person has been all shift." operationId: get_patrol_unit responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolUnitList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the fleet with each unit's position trail, which is what the map draws. tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/unit/near: get: description: "Answers which units can reach a site soonest. Distance is a great-circle\nhaversine over the units' last\ \ reported positions, and an available unit sorts\nahead of a busy one at any distance \u2014 a controller asking\ \ for the nearest unit\nis asking for the nearest free one." operationId: get_patrol_unit_near parameters: - description: Site is the site to measure from, by document name. in: query name: site required: false schema: type: string - description: 'Limit caps how many units come back. Anything that is not a positive integer uses 3, and higher values are clamped to 10.' in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolNearList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Answers which units can reach a site soonest. tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/unit/{name}/fix: post: description: 'Records where a unit is. It appends the position to the unit''s trail, moves the unit''s own coordinates and last-seen stamp, and publishes one frame to every screen watching. An officer may report only their own unit; the control room may report any.' operationId: post_patrol_unit_by_name_fix parameters: - description: Name is the unit's call sign, from the path. in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/patrol.patrolFixIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolFixOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records where a unit is. tags: - patrol x-app: patrol x-public: true x-tool: true /v1/patrol/unit/{name}/state: post: description: "Moves a unit between states \u2014 on shift, on a break, off, or in\ntrouble. An officer may move only\ \ their own unit; the control room may move\nany." operationId: post_patrol_unit_by_name_state parameters: - description: Name is the unit's call sign, from the path. in: path name: name required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/patrol.patrolUnitStateIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/patrol.patrolUnitOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Moves a unit between states \u2014 on shift, on a break, off, or in trouble." tags: - patrol x-app: patrol x-public: true x-tool: true /v1/plan: get: description: 'Returns the Hanzo cloud plan catalog: every cloud tier with its price, included capacity, limits and feature list, scoped to the caller''s catalog. A reseller org sees its own overrides in place of the canonical records it has replaced, and the canonical record for every tier it has not.' operationId: get_plan responses: '200': content: application/json: schema: $ref: '#/components/schemas/plan.planList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the Hanzo cloud plan catalog: every cloud tier with its price, included capacity, limits and feature list, scoped to the caller''s catalog.' tags: - plan x-app: plan x-public: true x-tool: true /v1/plan/blockchain: get: description: "Returns the blockchain RPC plan catalog: the tiers metered\nin monthly compute units, with their prices,\ \ limits and overage terms. It is\nthe canonical catalog for every caller \u2014 these plans carry no reseller\noverrides." operationId: get_plan_blockchain responses: '200': content: application/json: schema: $ref: '#/components/schemas/plan.planList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the blockchain RPC plan catalog: the tiers metered in monthly compute units, with their prices, limits and overage terms.' tags: - plan x-app: plan x-public: true x-tool: true /v1/plan/dns: get: description: "ListDNSPlans returns the DNS plan catalog: the tiers priced on zones, records\nper zone and queries per\ \ day. It is the canonical catalog for every caller \u2014\nthese plans carry no reseller overrides." operationId: get_plan_dns responses: '200': content: application/json: schema: $ref: '#/components/schemas/plan.planList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'ListDNSPlans returns the DNS plan catalog: the tiers priced on zones, records per zone and queries per day.' tags: - plan x-app: plan x-public: true x-tool: true /v1/plan/entitlements/{id}: get: description: "Returns what one plan GRANTS and not what it costs: the\ncanonical namespaced entitlement block and the\ \ flat license-feature list\nderived from it. It is the entitlement half of ResolvePlan, over the same\ncatalog and\ \ the same 404 for an id no catalog holds \u2014 the read a licensing or\nquota gate makes." operationId: get_plan_entitlements_by_id parameters: - description: "ID is the plan's catalog id or slug \u2014 \"dev\", \"max\", \"team\", \"rpc-growth\".\nBoth are matched,\ \ so a slug resolves the plan it names. A withdrawn id still\nresolves for a renewal, which is why this takes an\ \ id rather than a ladder\nposition." example: team in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/plan.planEntitlements' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns what one plan GRANTS and not what it costs: the canonical namespaced entitlement block and the flat license-feature list derived from it.' tags: - plan x-app: plan x-public: true x-tool: true /v1/plan/gpu: get: description: 'ListGPUTiers returns the rentable GPU configurations, each with its accelerator count and model, VRAM, vCPUs, host memory and hourly price.' operationId: get_plan_gpu responses: '200': content: application/json: schema: $ref: '#/components/schemas/plan.planTierList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: ListGPUTiers returns the rentable GPU configurations, each with its accelerator count and model, VRAM, vCPUs, host memory and hourly price. tags: - plan x-app: plan x-public: true x-tool: true /v1/plan/health: get: description: 'Reports that the plans subsystem is mounted and serving. It answers from the process itself and consults neither the catalog bundle nor the goja host, so it stays "ok" while either is degraded.' operationId: get_plan_health responses: '200': content: application/json: example: service: plans status: ok schema: $ref: '#/components/schemas/plan.planHealth' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports that the plans subsystem is mounted and serving. tags: - plan x-app: plan x-public: true x-tool: true /v1/plan/policy: get: description: 'Returns the published pricing policy: whether pricing is transparent, the revenue-sharing terms (idle compute resale and the open-source share) and the principles the catalog is priced by.' operationId: get_plan_policy responses: '200': content: application/json: schema: additionalProperties: {} type: object description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the published pricing policy: whether pricing is transparent, the revenue-sharing terms (idle compute resale and the open-source share) and the principles the catalog is priced by.' tags: - plan x-app: plan x-public: true x-tool: true /v1/plan/regions: get: description: 'Returns the regions cloud capacity is offered in, each with its display name and physical location.' operationId: get_plan_regions responses: '200': content: application/json: schema: $ref: '#/components/schemas/plan.planRegionList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the regions cloud capacity is offered in, each with its display name and physical location. tags: - plan x-app: plan x-public: true x-tool: true /v1/plan/resolve/{id}: get: description: 'Resolves one plan to everything a consumer of the catalog needs at once: its canonical entitlement block, the flat license-feature list a signed license carries, its billing reference, and the catalog it came from. The id may be the plan''s id or its slug, and it is resolved against the caller''s catalog, so a reseller''s override wins over the canonical record. An id no catalog holds answers 404.' operationId: get_plan_resolve_by_id parameters: - description: "ID is the plan's catalog id or slug \u2014 \"dev\", \"max\", \"team\", \"rpc-growth\".\nBoth are matched,\ \ so a slug resolves the plan it names. A withdrawn id still\nresolves for a renewal, which is why this takes an\ \ id rather than a ladder\nposition." example: dev in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/plan.planResolution' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Resolves one plan to everything a consumer of the catalog needs at once: its canonical entitlement block, the flat license-feature list a signed license carries, its billing reference, and the catalog it came from.' tags: - plan x-app: plan x-public: true x-tool: true /v1/plan/schema: get: description: 'Returns the two JSON Schema documents this surface speaks: entitlements.schema.json, which declares every entitlement key with its type, unit and enum, and plan.schema.json, which a catalog plan record conforms to.' operationId: get_plan_schema responses: '200': content: application/json: schema: $ref: '#/components/schemas/plan.planSchemas' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the two JSON Schema documents this surface speaks: entitlements.schema.json, which declares every entitlement key with its type, unit and enum, and plan.schema.json, which a catalog plan record conforms to.' tags: - plan x-app: plan x-public: true x-tool: true /v1/plan/storage: get: description: 'Returns the block-storage price block: the price per GB per month and the volume size bounds a cloud plan may attach.' operationId: get_plan_storage responses: '200': content: application/json: schema: additionalProperties: {} type: object description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the block-storage price block: the price per GB per month and the volume size bounds a cloud plan may attach.' tags: - plan x-app: plan x-public: true x-tool: true /v1/plan/subscriptions: get: description: "Returns the subscription ladder \u2014 the personal and team\ntiers a customer buys to use the cloud,\ \ each with its monthly and annual price,\nseat rules, limits and billing reference. Scoped to the caller's catalog." operationId: get_plan_subscriptions responses: '200': content: application/json: schema: $ref: '#/components/schemas/plan.planList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the subscription ladder \u2014 the personal and team tiers a customer buys to use the cloud, each\ \ with its monthly and annual price, seat rules, limits and billing reference." tags: - plan x-app: plan x-public: true x-tool: true /v1/plan/tools: get: description: "Returns the per-use price of every metered tool \u2014 web search,\ncode interpreter, image generation,\ \ speech \u2014 each with the unit it is billed\nin." operationId: get_plan_tools responses: '200': content: application/json: schema: $ref: '#/components/schemas/plan.planToolList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the per-use price of every metered tool \u2014 web search, code interpreter, image generation, speech\ \ \u2014 each with the unit it is billed in." tags: - plan x-app: plan x-public: true x-tool: true /v1/plan/vocab: get: description: 'Returns the entitlement key vocabulary: every key with its namespace, JSON type, nullability, unit, enum and title, the list of namespaces, and the engine features a license can grant. It is derived from entitlements.schema.json on every call, so it cannot fall behind the schema.' operationId: get_plan_vocab responses: '200': content: application/json: schema: $ref: '#/components/schemas/plan.planVocab' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the entitlement key vocabulary: every key with its namespace, JSON type, nullability, unit, enum and title, the list of namespaces, and the engine features a license can grant.' tags: - plan x-app: plan x-public: true x-tool: true /v1/platform/apps: get: description: "Answers what this organisation has declared, joined with what\nthe delivery plane has done about it.\n\ \nThe join is best-effort BY DESIGN and says so when it is missing: the\ndeclarations ARE the answer to \"what have\ \ I deployed\", so refusing the whole\nboard because the cluster is unreadable would lose the half that is readable.\n\ What must never happen is a silent null \u2014 an unreadable plane is reported as\n`cd.unavailable` carrying the reason,\ \ never as an app with no reconciliation,\nand a values file that does not parse is listed in `unreadable`, never\ \ dropped." operationId: get_platform_apps parameters: - description: 'Org names the organisation whose declarations to read, defaulting to the caller''s own. Only a SuperAdmin may name one that is not theirs; anyone else naming a foreign org is refused, so this widens nothing by itself.' in: query name: org required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.declaredResp' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Answers what this organisation has declared, joined with what the delivery plane has done about it. tags: - platform x-app: platform x-public: true x-tool: true post: description: "Builds a git repository into an image and writes the declaration that names it \u2014 a values file in\ \ `hanzoai/universe` under `charts/app/values//.yaml`, which the `fleet` ApplicationSet renders as\ \ one Application. That file IS the deployment: nothing else has to be applied.\n\n`repo` is a GitHub repository \u2014\ \ `owner/name` or `https://github.com/owner/name` \u2014 or another https clone URL. A GitHub repository must be one\ \ the caller may see: their own connected GitHub, or the installations their org holds for an org admin (the same\ \ set `GET /v1/provider/github/repos` lists); anything else is 404. It is built at the name GitHub gives it now, from\ \ `ref`, which defaults to its default branch. Any other URL is an org admin's to build.\n\n`partOf` names the project\ \ the app belongs to, and defaults to the app's own name.\n\nEvery member of the org may deploy; WHERE it lands is\ \ the difference. An org admin commits to universe main and CD applies it \u2014 in one call, naming the tag its own\ \ build will push, which CD pulls once the build lands. Anyone else opens a review: the declaration is pushed to `deploy///`,\ \ `declaration.review` is the link that opens the pull request, and nothing deploys until it is merged. `mode` may\ \ say `branch` to make an admin's deploy a review too; a non-admin naming `commit` is refused, never downgraded.\n\ \nA member's deploy is bounded by who asked: one build at a time and never the org's last build slot, which is kept\ \ for its admins; ten deploys an hour; five open review branches. Past a bound it answers 429. A review branch is\ \ deleted once main declares its tag, or after seven days unmerged; `notice` says so, and that the public `env` values\ \ the branch carries are cleartext in universe, where deleting the branch does not unpublish them.\n\nA release of\ \ an image an earlier call built (`tag` given) is proven pullable before main is pointed at it \u2014 a declaration\ \ naming an image the registry cannot serve is an ImagePullBackOff with no rollback path.\n\nOmit `tag` to build;\ \ give it to declare an image an earlier call already built, which is how a green build is released without rebuilding\ \ it. That release is an org admin's: a member's deploy always builds, and carries public `env` only \u2014 a sealed\ \ value would write the running app's secrets before the review is merged, so a member naming one is refused.\n\n\ An org is its name: the values DIRECTORY, the destination NAMESPACE and the AppProject FENCE are all ``, and\ \ the image is `//`. None of them is a request field \u2014 the directory decides what CD admits\ \ the sync under and the repository decides what the cluster pulls, so a caller who could name either could reach\ \ outside its own org.\n\n`org` is an ACT-AS, not a placement field: it defaults to the caller's own, and naming another\ \ requires SuperAdmin. So does naming a RESERVED org \u2014 the platform's own namespace family (the brands and their\ \ environments, the control and delivery planes, `admin`) \u2014 even when it is the caller's own, because an IAM\ \ org named `kube-system` does not own Kubernetes. Both refuse rather than downgrade, so an escape attempt is never\ \ indistinguishable from a normal request.\n\nA host outside the caller's org subtree is refused: claim and verify\ \ a custom domain first." operationId: post_platform_apps requestBody: content: application/json: schema: $ref: '#/components/schemas/declareReq' responses: 2XX: content: application/json: schema: $ref: '#/components/schemas/declareResp' description: Success summary: Deploy an app through cd.hanzo.ai tags: - platform x-app: platform x-public: true /v1/platform/apps/{app}: get: description: "Answers ONE declaration \u2014 what git says this app is, before the\ndelivery plane has had any say in\ \ it." operationId: get_platform_apps_by_app parameters: - description: "App is the DNS-1123 label of the declaration. The URL is the addressing\nauthority \u2014 a path segment\ \ binds after the body and after the query \u2014 so\nthe address decides which app is read whatever else is sent." in: path name: app required: true schema: type: string - description: 'Org names the organisation the declaration lives in, defaulting to the caller''s own and subject to the same SuperAdmin rule as the listing.' in: query name: org required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.Declaration' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Answers ONE declaration \u2014 what git says this app is, before the delivery plane has had any say in it." tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/apps/{app}/cd: get: description: "Answers ONE app's reconciliation alone \u2014 the poll a deploy\nconsole makes while it waits, without\ \ re-reading the whole inventory each time." operationId: get_platform_apps_by_app_cd parameters: - description: "App is the DNS-1123 label of the declaration. The URL is the addressing\nauthority \u2014 a path segment\ \ binds after the body and after the query \u2014 so\nthe address decides which app is read whatever else is sent." in: path name: app required: true schema: type: string - description: 'Org names the organisation the declaration lives in, defaulting to the caller''s own and subject to the same SuperAdmin rule as the listing.' in: query name: org required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.CDApp' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Answers ONE app's reconciliation alone \u2014 the poll a deploy console makes while it waits, without re-reading\ \ the whole inventory each time." tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/apps/{app}/diff: get: description: "Answers what syncing one app would change.\n\nIt reads the CD Application the declaration renders as:\ \ the commit CD compared\nthe cluster against, the commit it last applied, and every object it manages\nwith CD's\ \ verdict on each \u2014 the ones that differ from git are what a sync\napplies. Object-level, as CD reports it; the\ \ field-level diff stays on\ncd.hanzo.ai. SuperAdmin only, the same predicate CD's own RBAC applies to\nviewing an\ \ Application. 404 when CD holds no Application for the declaration." operationId: get_platform_apps_by_app_diff parameters: - description: App is the declaration's name, from the path. in: path name: app required: true schema: type: string - description: 'Org is the values directory the declaration lives in, defaulting to the caller''s own org.' in: query name: org required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.cdDiff' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Answers what syncing one app would change. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/apps/{app}/project: put: description: "Moves an app to a project.\n\nIt sets the declaration's `partOf` \u2014 in its cluster overlay when the\ \ overlay\nsets the key, else in its values file \u2014 and nothing else. Naming a project\nnothing names yet creates\ \ it. `org` is the values directory the app lives in;\n`mode` as for create." operationId: put_platform_apps_by_app_project parameters: - description: App is the declaration's name, from the path. in: path name: app required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/platform.appMove' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.projectWrite' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Moves an app to a project. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/apps/{app}/sync: post: description: "Syncs one app now: CD applies the commit it compared, to this one\nApplication.\n\nEvery Application is\ \ manual \u2014 its AppProject admits only a manual sync \u2014 so this\nis the deploy step, requested the way cd.hanzo.ai\ \ requests one: an operation on\nthe Application, initiated by the caller, applying the compared commit with the\n\ Application's own sync options and no pruning. It answers the Application with\nthe operation queued; poll the app's\ \ cd read for how it ends. SuperAdmin only,\nthe predicate CD's own RBAC holds sync to, and recorded in the audit\ \ trail like\nevery mutation. 409 while another operation is queued, running or terminating,\nbefore CD has compared\ \ the Application with git, and when the Application\nchanges between the read and the request." operationId: post_platform_apps_by_app_sync parameters: - description: App is the declaration's name, from the path. in: path name: app required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/platform.appCD' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/platform.cdDiff' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Syncs one app now: CD applies the commit it compared, to this one Application.' tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/builds: get: description: "Returns real build records for your org.\n\nIt lists the org's BuildKit build records \u2014 the git build\ \ step behind a deploy \u2014\neach with the repo it built, the short commit, its status, when it started and\nhow\ \ long it took. These are real records or an honest empty list; a build appears\nhere because one ran, never because\ \ a page needed a row. Builds are created only\nby /deploy and the push-to-deploy hook. Requires a validated principal;\ \ 401\nwithout one." operationId: get_platform_builds responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.buildBoard' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns real build records for your org. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/cd: get: description: 'Answers every Application the delivery plane holds. Scoped to the namespaces the caller''s own validated org owns: the ROLE admits the caller and the tenant boundary is applied inside, so an admin of one org never observes another''s.' operationId: get_platform_cd responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.cdResp' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Answers every Application the delivery plane holds. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/environments: get: description: "Returns your deploy targets, and what is running on each.\n\nIt returns the org's environments \u2014\ \ the distinct deploy targets its applications\nname, `production` for anything that names none \u2014 each aggregating\ \ the apps that\ntarget it, a rolled-up status and when it last changed.\n\nAn environment is DERIVED, not stored:\ \ there is nothing to create or delete here,\nand an environment exists exactly as long as an app points at it. Requires\ \ a\nvalidated principal; 401 without one." operationId: get_platform_environments responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.environmentBoard' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns your deploy targets, and what is running on each. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/fleet: get: description: "Returns the platform's own service tier, and where it has drifted.\n\nIt returns the board for the services\ \ the PLATFORM itself runs \u2014 iam, kms,\ngateway and the rest \u2014 as `{apps, summary}`: per service its environment,\ \ health,\nphase, the image tag its CR DECLARES, the tag actually running, and the drift\nbetween them, plus a summary\ \ counting the board green, yellow and red.\n\nThis is not a customer surface. `/v1/platform/projects/:project/apps`\ \ is a\ntenant's apps; this is the tier those tenants run ON, which is why the two are\nnamed differently rather than\ \ sharing a prefix.\n\nAdmission is scoped at the SCAN, before any CR is read: a platform SuperAdmin\nobserves the\ \ whole fleet, an org admin observes only their own org's namespaces,\nand an org that owns none gets an empty board\ \ \u2014 a non-super caller never even\nlists another org's services. Narrow further with `env`, `health`, `org`,\ \ or\n`drift=1` for only what has drifted.\n\nIt degrades honestly rather than failing whole: a namespace that does\ \ not exist\nis skipped, and a running-state read the caller cannot make leaves the running\ntag empty \u2014 an unknown,\ \ never a guess \u2014 while the declared, health and phase\ncolumns still render." operationId: get_platform_fleet parameters: - description: 'Env narrows to one lifecycle env: main, test or dev.' in: query name: env required: false schema: type: string - description: 'Health narrows to one health colour: green, yellow or red.' in: query name: health required: false schema: type: string - description: Org narrows to one image namespace. in: query name: org required: false schema: type: string - description: "Drift is `1` or `true` to show only rows that have actually drifted. It is\na STRING and not a bool\ \ because those two spellings are exactly what the\nboard has always accepted, and a bool would silently widen that\ \ to `?drift`\nalone and to `TRUE` \u2014 a behaviour change wearing a type change's clothes." in: query name: drift required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.driftBoard' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the platform's own service tier, and where it has drifted. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/fleet/{app}: get: description: "Returns one platform service, resolved to production by default.\n\nIt returns a single platform service\ \ by its CR name, with the same\ndeclared-versus-running and drift facts the board carries. The name must be a\nDNS-1123\ \ label; anything else is 400.\n\nNamespaces are scanned in lifecycle order \u2014 main, then test, then dev \u2014\ \ and the\nfirst match wins, so a bare name resolves to PRODUCTION. The scan covers only the\nnamespaces the caller\ \ is authorized for, so an org admin can never read a service\noutside their own org, and a name found in none of\ \ them is 404 rather than a leak." operationId: get_platform_fleet_by_app parameters: - description: App is the service's CR name, from the path. It must be a DNS-1123 label. in: path name: app required: true schema: type: string - description: 'Env narrows the scan to one lifecycle env: main, test or dev. Omitted, the namespaces are scanned in lifecycle order and the first match wins, so a bare name resolves to PRODUCTION.' in: query name: env required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.AppView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one platform service, resolved to production by default. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/fleet/{app}/deploy: post: description: "Rolls a platform service's pods, in a named environment.\n\nIt triggers a rolling restart of one platform\ \ service's Deployment by stamping a\nfresh restart annotation, and answers 202 with the app, the namespace, the\n\ environment and the timestamp. It restarts pods; it does NOT change the image \u2014 a\nversion change is the release\ \ path, not this.\n\nSuperAdmin ONLY, and deliberately narrower than the read gate beside it. The only\nnamespaces\ \ this board touches are the platform's own tier, so a restart here\nrecycles a SHARED service every tenant depends\ \ on. A brand-org admin is a\ncustomer-org admin, not a platform operator: observing the board is bounded and\naudited,\ \ and restarting production identity is not.\n\n`?env=main|test|dev` is REQUIRED \u2014 a bare call does not default\ \ to production,\nwhich is what closes the fat-finger and confused-deputy hazard \u2014 and any other\nvalue is 400.\ \ A service with no Deployment to restart in that environment is 404." operationId: post_platform_fleet_by_app_deploy parameters: - description: App is the service's CR name, from the path. It must be a DNS-1123 label. in: path name: app required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/platform.restartRef' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/platform.restarted' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Rolls a platform service's pods, in a named environment. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/health: get: description: "Reports whether this control plane can actually deploy anything.\n\nA real probe, not a status page. It\ \ answers 200 only when the metadata store is\nopen AND the cluster is genuinely reachable \u2014 proved by LISTING\ \ the operator App\nCRD, which settles reachability and CRD presence in one bounded call, and which\nis the exact\ \ question every deploy depends on. Anything else is 503 carrying the\nreal reason and whether the CRD was found.\n\ \nA constructed cluster client proves nothing \u2014 it is built from a kubeconfig, not\nfrom a reachable apiserver\ \ \u2014 so this deliberately spends a round trip rather than\nreporting `ok` while every deploy fails. Not admin-gated:\ \ liveness has to be\nprobe-able without a credential." operationId: get_platform_health responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.readiness' description: ok '503': content: application/json: schema: $ref: '#/components/schemas/platform.readiness' description: service unavailable default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports whether this control plane can actually deploy anything. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/pipelines: get: description: "Returns one build-and-deploy pipeline per app, with its latest run.\n\nIt returns one pipeline per application\ \ in the caller's org \u2014 its repo or image\nsource, its current status, and when its most recent deployment ran\ \ and how long\nit took. A pipeline is a PROJECTION of an app plus its newest deployment, not a\nseparate record:\ \ it comes into existence with the app and is triggered only\nthrough /deploy, never here. Requires a validated principal;\ \ 401 without one." operationId: get_platform_pipelines responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.pipelineBoard' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one build-and-deploy pipeline per app, with its latest run. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects: get: description: 'Answers every project the caller may see, with how its apps stand. A project is the `partOf` its apps'' values files name, so this is the declaration inventory grouped by that key, joined per app with CD''s reconciliation, the pods the cluster runs and the newest release the registry publishes, and folded into counts: apps, namespaces, sync, health and drift. Declarations that name no project are listed separately, because each one is drift in git. A SuperAdmin sees every owner''s projects, the platform''s own among them; an org admin sees only its own org''s. `org` narrows a SuperAdmin to one owner. A plane that cannot be read leaves its counts at unknown and says why in cdUnavailable or clusterUnavailable; it never empties the board. A values file that does not parse is listed in unreadable and costs only its own row.' operationId: get_platform_projects parameters: - description: 'Org names whose projects to read. Omitted, a SuperAdmin reads every owner''s and anyone else reads their own; naming another org is an act-as only a SuperAdmin has, and naming a platform directory reads the platform''s own.' in: query name: org required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.projectBoard' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Answers every project the caller may see, with how its apps stand. tags: - platform x-app: platform x-public: true x-tool: true post: description: "Creates a project from the apps it starts with.\n\nA project is the `partOf` its apps' values files name,\ \ so creating one names it\non the listed apps: one commit to `hanzoai/universe` that moves each file's\n`partOf`\ \ scalar and touches nothing else. There is no empty project; to start\none with a new app, create the app with `partOf`\ \ (POST /v1/platform/apps).\n\n`mode` is `branch` (the default: a review branch, nothing deploys) or `commit`\n(main).\ \ A `partOf` change relabels the app's pods, so the sync that applies it\nrolls them. 409 when the name already names\ \ apps \u2014 move apps into an existing\nproject with PUT /v1/platform/apps/{app}/project. An org admin changes its\ \ own\norg's projects; the platform's own, and another org's, are SuperAdmin." operationId: post_platform_projects requestBody: content: application/json: schema: $ref: '#/components/schemas/platform.projectCreate' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/platform.projectWrite' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a project from the apps it starts with. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects/{project}: delete: description: 'Folds a project into another. An app always belongs to exactly one project, so a project ends by moving its apps: `into` names the existing project they move to, and the project is gone once no file names it. One commit to `hanzoai/universe`; `mode` as for create. 404 when `into` is not a project.' operationId: delete_platform_projects_by_project parameters: - description: Project is the project to delete, from the path. in: path name: project required: true schema: type: string - description: 'Into is the existing project its apps move into. Required: an app always belongs to exactly one project.' in: query name: into required: false schema: type: string - description: Org names the projects' owner, defaulting to the caller's own scope. in: query name: org required: false schema: type: string - description: Mode is `branch` (the default) or `commit`. in: query name: mode required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.projectWrite' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Folds a project into another. tags: - platform x-app: platform x-public: true x-tool: true get: description: "Answers one project and every app in it.\n\nEach app carries its declaration \u2014 image, hosts, replicas,\ \ the KMS paths it\nreads (never a value) \u2014 together with the CD Application reconciling it, what\nits pods run,\ \ the newest release its registry publishes, and the drift between\nthose four. 404 when no declaration in the caller's\ \ scope names the project." operationId: get_platform_projects_by_project parameters: - description: Project is the project's name, from the path. in: path name: project required: true schema: type: string - description: "Org names the project's owner, defaulting to the caller's own scope \u2014 which\nfor a SuperAdmin whose\ \ home is a brand org is the platform's." in: query name: org required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.projectView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Answers one project and every app in it. tags: - platform x-app: platform x-public: true x-tool: true put: description: 'Renames a project. It rewrites `partOf` on every declaration that names the project, in one commit to `hanzoai/universe`. 409 when the new name is already a project: fold into it with DELETE /v1/platform/projects/{project}?into=. `mode` as for create.' operationId: put_platform_projects_by_project parameters: - description: Project is the project to rename, from the path. in: path name: project required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/platform.projectRename' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.projectWrite' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Renames a project. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects/{project}/apps: get: description: "Returns the applications in one project, with what the cluster says\nabout them.\n\nIt lists the caller\ \ org's applications under one project. Each row carries the\nstored record and, for an app that is live or deploying,\ \ the LIVE phase and\nhealth read from its operator Service CR; an app with sealed env also carries\nits secret-sync\ \ state. Those cluster reads are best-effort \u2014 an unreachable\ncluster leaves those fields empty and never blocks\ \ the listing.\n\nThe project must exist in IAM for this org, or the answer is 404; the `default`\nproject is implicit\ \ and always accepted, because it is part of what an org IS.\nRequires a validated principal; 401 without one." operationId: get_platform_projects_by_project_apps parameters: - description: Project is the project's name, from the path. in: path name: project required: true schema: type: string responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/platform.appOut' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the applications in one project, with what the cluster says about them. tags: - platform x-app: platform x-public: true x-tool: true post: description: "Creates an application from a git repo or a container image.\n\nIt registers a new application under one\ \ of the caller org's projects and\nanswers 201 with it. Creating does NOT deploy: the app lands in `draft` and\n\ nothing reaches the cluster until /deploy.\n\n`source` is `git` \u2014 which requires `repo.url` \u2014 or `image`,\ \ which requires\n`image.repository`; anything else is 400. A git app builds with zero-config\n`pack` by default and\ \ may opt into `dockerfile`; an image app never builds. The\nrepo URL and Dockerfile path are validated here against\ \ the SAME allowlist the\nprivileged build enforces, so an unsafe source is refused before it is ever\npersisted.\n\ \nThe `slug` is the app's identity in the cluster: given or derived from `name`,\nit must match `^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$`,\ \ and a slug already used in\nthis project is 409. `replicas` and `storageGb` are clamped to the deployment's\nlimits\ \ rather than refused.\n\nEnv keys must match `^[A-Za-z_][A-Za-z0-9_]*$`. A variable marked `secret: true`\nis SEALED\ \ into KMS and its plaintext is never written to the database \u2014 and if\nKMS is unavailable the create fails 503\ \ rather than falling back to storing a\nsecret in the clear.\n\nThe app is seeded with its canonical default host,\ \ so it has a working HTTPS URL\nthe moment it deploys. A bare custom domain cannot be attached here \u2014 it has\ \ to\ngo through add-domain and DNS verification first. Requires a validated\nprincipal; 401 without one, and every\ \ cluster object it will later create lands\nin that org's own `tenant-` namespace." operationId: post_platform_projects_by_project_apps parameters: - description: Project is the project to create the application under, from the path. in: path name: project required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/platform.createAppReq' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/platform.appOut' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates an application from a git repo or a container image. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects/{project}/apps/{app}: delete: description: "Deletes an application and tears down what it runs.\n\nIt removes the application record and tears down\ \ what it owns in the org's\ntenant namespace \u2014 its operator Service CR and its KMSSecret \u2014 then answers\ \ 204.\nAn app this org and project do not have is 404, never a silent success.\n\nTeardown is best-effort by design:\ \ a cluster that refuses or is unreachable does\nnot block the delete, so the record cannot be left orphaned behind\ \ a broken\ncluster; the failure is logged for operators and the orphan reaper reconciles\nit. Requires a validated\ \ principal; 401 without one." operationId: delete_platform_projects_by_project_apps_by_app parameters: - description: Project is the project the application lives under, from the path. in: path name: project required: true schema: type: string - description: App is the application's slug, from the path. in: path name: app required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes an application and tears down what it runs. tags: - platform x-app: platform x-public: true x-tool: true get: description: 'Returns one application, with its live phase, health and secret sync. It returns a single application of the caller''s org together with what the cluster currently reports for it: the operator Service CR''s phase and health, and whether its sealed env has synced. An app this org and project do not have is 404. Requires a validated principal; 401 without one.' operationId: get_platform_projects_by_project_apps_by_app parameters: - description: Project is the project the application lives under, from the path. in: path name: project required: true schema: type: string - description: App is the application's slug, from the path. in: path name: app required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.appOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one application, with its live phase, health and secret sync. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects/{project}/apps/{app}/deploy: post: description: "Deploys the app \u2014 building it first if it comes from git.\n\nIt starts a new, monotonically versioned\ \ deployment of the app and answers 202\nwith the deployment record. A 202 is an ACCEPTED deployment, not a live one.\n\ \nAn IMAGE app deploys the tag you name (falling back to the app's tag, then\n`latest`) by writing its operator Service\ \ CR; the operator reconciles it to\nrunning. A GIT app launches an in-cluster BuildKit Job at `commit` \u2014 or\ \ the app's\nbranch \u2014 and comes back in `building`; the Service CR is applied later, by the\nreconciler, once\ \ the Job succeeds. The reconciler is restart-safe, so a build in\nflight survives a cloud restart.\n\nDeploys are\ \ bounded per org: over the concurrent-deploy cap is 429 and NOTHING is\nrecorded, so a rejected deploy leaves no\ \ phantom in the history. An unreachable\ncluster is 503 but still records an honest `error` deployment, because a\ \ deploy\nthat was attempted and failed must not be indistinguishable from one never made.\nEvery other failure is\ \ likewise recorded in its real terminal state.\n\nThis is metered work: a git build is billed to the org's ledger\ \ in wall-clock\nbuild minutes once the Job finishes, and the running deployment is billed for its\ncompute per tick\ \ for as long as it stays live. Requires a validated principal; 401\nwithout one, and everything is written into that\ \ org's own `tenant-`\nnamespace." operationId: post_platform_projects_by_project_apps_by_app_deploy parameters: - description: Project is the project the application lives under, from the path. in: path name: project required: true schema: type: string - description: App is the application's slug, from the path. in: path name: app required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/platform.deployReq' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/platform.deploymentView' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Deploys the app \u2014 building it first if it comes from git." tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects/{project}/apps/{app}/deployments: get: description: "Returns an app's deployment history.\n\nIt lists every deployment recorded for one of the caller org's\ \ applications,\nnewest version first, each with its version, status, source, commit and image.\nFailed and superseded\ \ attempts are included \u2014 that is the point of a history.\nRequires a validated principal; 401 without one." operationId: get_platform_projects_by_project_apps_by_app_deployments parameters: - description: Project is the project the application lives under, from the path. in: path name: project required: true schema: type: string - description: App is the application's slug, from the path. in: path name: app required: true schema: type: string responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/platform.deploymentView' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns an app's deployment history. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects/{project}/apps/{app}/deployments/{id}: get: description: "Returns one deployment of one app.\n\nIt returns a single deployment by id, scoped to the named application\ \ of the\ncaller's org \u2014 so an id belonging to another app or another tenant is 404, not a\nread. Requires a\ \ validated principal; 401 without one." operationId: get_platform_projects_by_project_apps_by_app_deployments_by_id parameters: - description: Project is the project the application lives under, from the path. in: path name: project required: true schema: type: string - description: App is the application's slug, from the path. in: path name: app required: true schema: type: string - description: ID is the deployment's id, from the path. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.deploymentView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one deployment of one app. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects/{project}/apps/{app}/deployments/{id}/logs: get: description: "Returns real logs for a deployment \u2014 the build's, then the app's.\n\nIt returns the deployment's\ \ recorded status timeline together with LIVE pod logs\npulled from the cluster: the build pod's output while a git\ \ build is running, and\nthe running app's output once it is deployed. The `source` field says which of\nthe two the\ \ body is \u2014 `build`, `app` or `none` \u2014 so a console can label the pane\nhonestly.\n\nIt never fabricates\ \ log content. When no pod exists yet, or the cluster is\nunreachable, it degrades to the recorded timeline and says\ \ so. Every cluster read\nis confined to the caller org's own namespaces and time-boxed. Requires a\nvalidated principal;\ \ 401 without one." operationId: get_platform_projects_by_project_apps_by_app_deployments_by_id_logs parameters: - description: Project is the project the application lives under, from the path. in: path name: project required: true schema: type: string - description: App is the application's slug, from the path. in: path name: app required: true schema: type: string - description: ID is the deployment's id, from the path. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.deployLogs' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns real logs for a deployment \u2014 the build's, then the app's." tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects/{project}/apps/{app}/domains: get: description: 'Returns every hostname this app answers on. It lists the app''s hosts: the permanent default host it was born with, any org-subtree hosts attached to it, and every custom host claimed for it with its verification state and, while pending, the DNS challenge records to publish. Live endpoint status for each host is observed from the cluster. Requires a validated principal; 401 without one.' operationId: get_platform_projects_by_project_apps_by_app_domains parameters: - description: Project is the project the application lives under, from the path. in: path name: project required: true schema: type: string - description: App is the application's slug, from the path. in: path name: app required: true schema: type: string responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/platform.domainView' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns every hostname this app answers on. tags: - platform x-app: platform x-public: true x-tool: true post: description: "Attaches a hostname \u2014 instantly if you already own it, otherwise with a\nDNS challenge.\n\nIt attaches\ \ `host` to the app, and which of two things happens depends on who\nowns the name. A host inside the caller org's\ \ own subtree is structurally owned,\nso it goes ACTIVE immediately and answers 201. A bring-your-own host is claimed\n\ as PENDING and answers the DNS challenge records to publish; it is NOT rendered\ninto the app's ingress until /verify\ \ passes.\n\nClaims are globally unique. A host already claimed by another organization is\n409, and so is one claimed\ \ by a different app in your own; re-adding this app's\nOWN claim is idempotent and answers its current state at 200.\ \ The default host is\nalways attached and re-adding it is 409. A host under the platform's shared apex\nthat is not\ \ the caller's own subtree is 403 \u2014 it belongs to whoever owns that\nsubtree and can never be grabbed through\ \ the custom path.\n\n`host` must be a valid DNS hostname; anything else is 400. Requires a validated\nprincipal;\ \ 401 without one." operationId: post_platform_projects_by_project_apps_by_app_domains parameters: - description: Project is the project the application lives under, from the path. in: path name: project required: true schema: type: string - description: App is the application's slug, from the path. in: path name: app required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/platform.addDomainReq' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.domainView' description: ok '201': content: application/json: schema: $ref: '#/components/schemas/platform.domainView' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Attaches a hostname \u2014 instantly if you already own it, otherwise with a DNS challenge." tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects/{project}/apps/{app}/domains/{host}: delete: description: "Detaches a hostname and releases the claim.\n\nIt drops the host from the app's ingress and releases any\ \ custom claim on it, so\nthe name becomes claimable again \u2014 by this org or any other. Answers 204.\n\nThe default\ \ host is permanent and cannot be removed: that is 400, not 404. A host\nthat is neither attached nor claimed here\ \ is 404. Requires a validated principal;\n401 without one." operationId: delete_platform_projects_by_project_apps_by_app_domains_by_host parameters: - description: Project is the project the application lives under, from the path. in: path name: project required: true schema: type: string - description: App is the application's slug, from the path. in: path name: app required: true schema: type: string - description: Host is the hostname, from the path. in: path name: host required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Detaches a hostname and releases the claim. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects/{project}/apps/{app}/domains/{host}/verify: post: description: 'Checks a custom domain''s DNS and turns it on if it passes. It runs the DNS challenge check for a pending custom host and, when it passes, marks the host verified and renders it into the app''s ingress so it starts serving. A check that RAN and did not pass is not an error: it answers 200 with the host still pending and the reason in `detail`, so a console can show the operator what DNS is actually returning. An already-verified host answers as-is without re-checking. A host not claimed by this app is 404. Requires a validated principal; 401 without one.' operationId: post_platform_projects_by_project_apps_by_app_domains_by_host_verify parameters: - description: Project is the project the application lives under, from the path. in: path name: project required: true schema: type: string - description: App is the application's slug, from the path. in: path name: app required: true schema: type: string - description: Host is the hostname, from the path. in: path name: host required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.domainView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Checks a custom domain's DNS and turns it on if it passes. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects/{project}/apps/{app}/env: put: description: "Replaces an app's environment variables.\n\nIt writes the app's whole environment set and answers the\ \ updated application.\nThis is the one post-create write path for env, and it REPLACES rather than\nmerges: a variable\ \ absent from the body is gone, and a secret dropped from the\nset leaves the app's Secret on its next deploy.\n\n\ Keys must match `^[A-Za-z_][A-Za-z0-9_]*$`. A value marked `secret: true` is\nsealed into KMS and blanked in the database,\ \ so plaintext is never persisted \u2014\nand the write fails 503 if KMS is unavailable rather than storing one in\ \ the\nclear.\n\nThe rule worth knowing: this does not restart anything. Once the app has been\ndeployed the secret\ \ sync is re-declared immediately so the operator\nre-materialises the Secret, but RUNNING pods keep the environment\ \ they started\nwith until their next deploy or restart. Requires a validated principal; 401\nwithout one." operationId: put_platform_projects_by_project_apps_by_app_env parameters: - description: Project is the project the application lives under, from the path. in: path name: project required: true schema: type: string - description: App is the application's slug, from the path. in: path name: app required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/platform.setEnvReq' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.appOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Replaces an app's environment variables. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects/{project}/apps/{app}/preview: post: description: "Puts a branch on its own URL.\n\nIt deploys an already-built `image` to a per-branch preview and answers\ \ its URL,\nthe branch, the preview's slug and the deployment. The preview is a FIRST-CLASS\napplication named `-`\ \ in the same project and tenant namespace, with\nits own default host \u2014 so it is completely isolated from production\ \ while reusing\nthe same deploy mechanic. Re-previewing a branch converges that same target in\nplace rather than\ \ stacking another one.\n\nIt carries NO environment variables, deliberately: a preview never inherits\nproduction's\ \ secrets. It also does not build \u2014 `image` is required and must\nalready exist, and `branch` defaults to the\ \ parent app's. A branch that does not\nresolve to a valid slug distinct from the parent's is 400. Requires a validated\n\ principal; 401 without one." operationId: post_platform_projects_by_project_apps_by_app_preview parameters: - description: Project is the project the parent application lives under, from the path. in: path name: project required: true schema: type: string - description: App is the parent application's slug, from the path. in: path name: app required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/platform.previewReq' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/platform.previewView' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Puts a branch on its own URL. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects/{project}/apps/{app}/promote: post: description: "Promotes an already-built release to the app.\n\nIt redeploys an image that already exists \u2014 named\ \ either by `deploymentId`, which\npromotes that deployment's exact built image, or by `tag`, resolved the same way\n\ a deploy resolves one. One of the two is required; neither is 400.\n\nPromotion never builds. A deployment that carries\ \ no built image cannot be\npromoted and is 400, and a deployment id outside this app is 404. It runs through\nthe\ \ same deploy core as everything else, so it takes a NEW version number and is\nsubject to the same per-org concurrency\ \ cap. Requires a validated principal; 401\nwithout one." operationId: post_platform_projects_by_project_apps_by_app_promote parameters: - description: Project is the project the application lives under, from the path. in: path name: project required: true schema: type: string - description: App is the application's slug, from the path. in: path name: app required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/platform.promoteReq' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/platform.deploymentView' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Promotes an already-built release to the app. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects/{project}/apps/{app}/rollback: post: description: "Goes back to the previous release.\n\nIt redeploys a prior image: the one named by `deploymentId`, or\ \ \u2014 with no body \u2014\nthe newest earlier deployment that carries a real built image and did not error,\nskipping\ \ the release currently live. An app with nothing earlier to return to is\n400.\n\nA rollback is a deploy of an old\ \ image, not a rewind: it takes a NEW version\nnumber and appends to the history rather than erasing what came after.\ \ Both\nlookups are scoped to this app and org, so another tenant's image can never be\nrolled in. Requires a validated\ \ principal; 401 without one." operationId: post_platform_projects_by_project_apps_by_app_rollback parameters: - description: Project is the project the application lives under, from the path. in: path name: project required: true schema: type: string - description: App is the application's slug, from the path. in: path name: app required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/platform.rollbackReq' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/platform.deploymentView' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Goes back to the previous release. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects/{project}/apps/{app}/start: post: description: 'Starts a stopped app back up. It scales the app''s Service back to its configured replica count and marks it live, answering the updated application. It does not redeploy: the image already on the Service CR is what comes back. The billing watermark is reset to now as part of starting, so the org is charged for THIS live span and never for the gap the app spent stopped. An app with no Service CR is 404, an unreachable cluster is 503, and a cluster that refuses the scale is 502. Requires a validated principal; 401 without one.' operationId: post_platform_projects_by_project_apps_by_app_start parameters: - description: Project is the project the application lives under, from the path. in: path name: project required: true schema: type: string - description: App is the application's slug, from the path. in: path name: app required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.appOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Starts a stopped app back up. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/projects/{project}/apps/{app}/stop: post: description: "Stops an app without deleting it.\n\nIt scales the app's Service to zero replicas and marks it stopped,\ \ answering the\nupdated application. Nothing else is removed \u2014 the record, its env, its domains\nand its deployment\ \ history all survive, and /start brings it back at the same\nreplica count.\n\nAn app that is not deployed has no\ \ Service CR to scale and is 404. An\nunreachable cluster is 503 and a cluster that refuses the scale is 502. Because\n\ the pods stop, so does the compute metering. Requires a validated principal; 401\nwithout one." operationId: post_platform_projects_by_project_apps_by_app_stop parameters: - description: Project is the project the application lives under, from the path. in: path name: project required: true schema: type: string - description: App is the application's slug, from the path. in: path name: app required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.appOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Stops an app without deleting it. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/releases: get: description: "Returns the versions that actually reached the cluster.\n\nIt lists the org's releases: the deployments\ \ that were genuinely applied to the\ncluster, with the app they belong to, their version, environment, status and\ \ when\nthey were released. A deployment that failed or is still building is NOT a\nrelease and is excluded \u2014\ \ reaching the cluster is what makes one. Requires a\nvalidated principal; 401 without one." operationId: get_platform_releases responses: '200': content: application/json: schema: $ref: '#/components/schemas/platform.releaseBoard' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the versions that actually reached the cluster. tags: - platform x-app: platform x-public: true x-tool: true /v1/platform/run: post: description: "Runs a container image and gives back a URL.\n\nThe one-call shortcut over project \u2192 app \u2192 deploy:\ \ give it a `name` and an\n`image` and it creates or updates an image-source application in your org's\nDEFAULT project,\ \ deploys it through the same operator Service-CR writer\neverything else uses, and answers its id, name, live URL,\ \ status and shape.\nRe-running the same name UPDATES it in place, so the call is idempotent by name.\n\nWhat it produces\ \ is a first-class application, not a special object: it is\nlistable, stoppable and redeployable through the /v1/platform\ \ routes like any\nother app.\n\n`minScale` is the replica floor. `maxScale` above it declares an autoscaling\nceiling;\ \ `maxScale: 0` means no autoscaler at all \u2014 a fixed run at the floor.\nBoth are clamped to the deployment's\ \ limits. `runtime` and `shape` are accepted\nfor the client contract and echoed back: the image is the runtime unit\ \ and sizing\nis the operator's default.\n\nIt is BILLING-GATED before it touches the cluster: a flat per-run fee\ \ is\nauthorized against the org's own prepaid balance first, so an org that cannot pay\nis refused without anything\ \ being created. An unreachable cluster is 503 \u2014 a run\nnever reports a URL it did not create. Secret env is\ \ sealed into KMS and fails\nclosed without it.\n\nRequires a validated principal; 401 without one. The org is resolved\ \ from that\nvalidated identity and is what both pays and owns the namespace \u2014 it is never\nread from the body." operationId: post_platform_run requestBody: content: application/json: schema: $ref: '#/components/schemas/platform.runReq' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/platform.runView' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Runs a container image and gives back a URL. tags: - platform x-app: platform x-public: true x-tool: true /v1/pref: get: description: "Returns the signed-in caller's OWN preference document \u2014 the theme,\ndensity and pinned nav that\ \ follow them across every Hanzo surface. There is no\npath to another user's preferences: not for an org admin, not\ \ for a platform\nSuperAdmin, because the subject is built from the validated credential and is the\nmandatory predicate\ \ on the read. A caller who has never saved anything gets an\nempty document at 200, never a 404, so the user menu\ \ always renders." operationId: get_pref responses: '200': content: application/json: schema: $ref: '#/components/schemas/pref.prefsView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the signed-in caller's OWN preference document \u2014 the theme, density and pinned nav that follow\ \ them across every Hanzo surface." tags: - pref x-app: pref x-public: true x-tool: true patch: description: "Merges a JSON object key-wise into the signed-in caller's OWN preference document and answers with the\ \ whole document after the merge, so a surface saves `theme` without having to send back the `density` another surface\ \ owns. The merge is SHALLOW and the key space is open: an unnamed key is left untouched, a named key is replaced\ \ whole, and a key sent with a `null` value is DELETED. The subject is the `/` identity built from the\ \ validated credential and is the mandatory predicate on the write, so there is no path to another user's preferences\ \ \u2014 not for an org admin, not for a platform SuperAdmin. Fails closed: no validated principal is 403; an empty\ \ body or a literal `null` is 400; and a patch or a resulting document over 16 KiB or 128 keys is 413." operationId: patch_pref summary: Save the preference keys your surface owns, leaving every other key alone tags: - pref x-app: pref x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/pricing: get: description: "Returns the whole pricing catalog in one document: Zen and\nthird-party models, providers, model families,\ \ the free-model list, plan and\ninfrastructure pricing. Every model and provider it names is filtered to what\nthe\ \ caller's org may see \u2014 the same gate the leaf routes apply, so this can\nnever be an un-gated second source\ \ for what they hide." operationId: get_pricing responses: '200': content: application/json: schema: additionalProperties: {} type: object description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the whole pricing catalog in one document: Zen and third-party models, providers, model families, the free-model list, plan and infrastructure pricing.' tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/base: get: description: "Returns the Hanzo Base plans \u2014 the managed-instance tiers,\neach with its monthly and annual price,\ \ storage and request allowances and\nfeature list." operationId: get_pricing_base responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.pricingPlanList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the Hanzo Base plans \u2014 the managed-instance tiers, each with its monthly and annual price, storage\ \ and request allowances and feature list." tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/blockchain: get: description: "Returns the blockchain access plans \u2014 the RPC and node\ntiers, each with its monthly price, compute-unit\ \ allowance and feature list." operationId: get_pricing_blockchain responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.pricingPlanList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the blockchain access plans \u2014 the RPC and node tiers, each with its monthly price, compute-unit\ \ allowance and feature list." tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/cloud: get: description: "Returns the public cloud section of the catalog in one\ndocument: its instance plans, its regions and\ \ its block-storage prices. The\nsection's internal half \u2014 the provider costs Hanzo pays and the plan-to-\nprovider\ \ routing table \u2014 is stripped before it is served, so this is what a\ncustomer may see and nothing more." operationId: get_pricing_cloud responses: '200': content: application/json: schema: additionalProperties: {} type: object description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the public cloud section of the catalog in one document: its instance plans, its regions and its block-storage prices.' tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/cloud/plans: get: description: "Returns just the cloud instance plans \u2014 each with its vCPU,\nmemory, disk, CPU type, VM allowance,\ \ feature list and monthly and hourly\nprice. It is the plans of the cloud section on their own." operationId: get_pricing_cloud_plans responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.pricingPlanList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns just the cloud instance plans \u2014 each with its vCPU, memory, disk, CPU type, VM allowance, feature\ \ list and monthly and hourly price." tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/cloud/regions: get: description: 'Returns the regions a cloud instance can be placed in, each with its id, display name and physical location. It is the regions of the cloud section on their own.' operationId: get_pricing_cloud_regions responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.pricingRegionList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the regions a cloud instance can be placed in, each with its id, display name and physical location. tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/cloud/storage: get: description: 'Returns the block-storage prices of the cloud section: the per-GB monthly rate and the volume size bounds a caller may ask for.' operationId: get_pricing_cloud_storage responses: '200': content: application/json: schema: additionalProperties: {} type: object description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the block-storage prices of the cloud section: the per-GB monthly rate and the volume size bounds a caller may ask for.' tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/compute: get: description: "Returns the compute section of the catalog: the cloud\nprovider and region the prices are quoted for,\ \ the monthly markup applied to\nthem, the full instance-size tier list and the named presets. It is the\nwhole section\ \ as the pricing source records it, un-gated \u2014 no model or\nprovider identity appears in it." operationId: get_pricing_compute responses: '200': content: application/json: schema: additionalProperties: {} type: object description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the compute section of the catalog: the cloud provider and region the prices are quoted for, the monthly markup applied to them, the full instance-size tier list and the named presets.' tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/compute/presets: get: description: "Returns just the named compute sizes \u2014 the short,\nhuman-labelled list (\"Starter\", \"Pro\") a size\ \ picker renders, each carrying\nits provider slug, vCPU, memory, disk and price. It is the presets of the\ncompute\ \ section on their own, for a caller that does not need the full tier\ntable." operationId: get_pricing_compute_presets responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.pricingPresetList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns just the named compute sizes \u2014 the short, human-labelled list (\"Starter\", \"Pro\") a size picker\ \ renders, each carrying its provider slug, vCPU, memory, disk and price." tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/datastore: get: description: "Returns the Hanzo Datastore rate card: the tier list, the\nper-GB storage and egress usage rates, the\ \ annual discount and the trial. It is\nthe section as authored, un-gated \u2014 no provider identity appears in it.\n\ \nThe route was missing while the data existed, so this 404d and every visitor to\nhanzo.ai's Infrastructure tab was\ \ told pricing was \"temporarily unavailable\"." operationId: get_pricing_datastore responses: '200': content: application/json: schema: additionalProperties: {} type: object description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the Hanzo Datastore rate card: the tier list, the per-GB storage and egress usage rates, the annual discount and the trial.' tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/enablement: get: description: "Returns what the caller's org can actually use: every managed\nitem with its global state, whether it\ \ is effective here, whether this org is\nalready opted into its beta, and whether it may still opt in. Read-only\ \ and\nsafe for any caller \u2014 one without a validated principal simply sees the\ngenerally-available items and\ \ no opt-in affordance, never another org's state." operationId: get_pricing_enablement responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.enablementBoard' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns what the caller''s org can actually use: every managed item with its global state, whether it is effective here, whether this org is already opted into its beta, and whether it may still opt in.' tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/enablement/optin: post: description: "Opts the caller's OWN org into a beta item. The org is the\ncaller's validated one, so this can never\ \ target another org, and the registry\nrefuses anything not in beta \u2014 so it can neither re-open an item an operator\n\ turned off nor touch one that is already generally available. Requires a\nsigned-in caller with an org." operationId: post_pricing_enablement_optin requestBody: content: application/json: example: id: labs kind: feature schema: $ref: '#/components/schemas/pricing.enablementOptRef' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.userEnablementItem' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Opts the caller's OWN org into a beta item. tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/enablement/optout: post: description: 'Removes the caller''s OWN org from a beta item''s grant list, the reverse of OptIntoBeta and idempotent. The org is the caller''s validated one, so this can never revoke another org''s grant. Requires a signed-in caller with an org.' operationId: post_pricing_enablement_optout requestBody: content: application/json: example: id: labs kind: feature schema: $ref: '#/components/schemas/pricing.enablementOptRef' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.userEnablementItem' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes the caller's OWN org from a beta item's grant list, the reverse of OptIntoBeta and idempotent. tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/featured: get: description: 'Returns the models the catalog highlights, filtered to what the caller''s org may see. It is the same catalog as ListModels narrowed to entries the pricing source marks featured.' operationId: get_pricing_featured responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.pricingModelList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the models the catalog highlights, filtered to what the caller's org may see. tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/free: get: description: 'Returns the models that cost nothing to call, filtered to what the caller''s org may see. It is the same catalog as ListModels narrowed to entries the pricing source marks free.' operationId: get_pricing_free responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.pricingModelList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the models that cost nothing to call, filtered to what the caller's org may see. tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/gpu: get: description: 'ListGPUTiers returns the rentable GPU configurations, each with its accelerator count and model, VRAM, vCPU, host memory and hourly price.' operationId: get_pricing_gpu responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.pricingTierList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: ListGPUTiers returns the rentable GPU configurations, each with its accelerator count and model, VRAM, vCPU, host memory and hourly price. tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/health: get: description: 'Reports that the pricing subsystem is mounted and serving. It answers from the process itself and consults neither the catalog bundle nor the enablement store, so it stays "ok" while either is degraded.' operationId: get_pricing_health responses: '200': content: application/json: example: service: pricing status: ok schema: $ref: '#/components/schemas/pricing.pricingHealth' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports that the pricing subsystem is mounted and serving. tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/iam: get: description: "ListIAMPlans returns the identity plans \u2014 the Hanzo IAM tiers, each with its\nmonthly and annual\ \ price, monthly-active-user allowance and feature list." operationId: get_pricing_iam responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.pricingPlanList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "ListIAMPlans returns the identity plans \u2014 the Hanzo IAM tiers, each with its monthly and annual price,\ \ monthly-active-user allowance and feature list." tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/model/{name}: get: description: "Returns one model's catalog entry \u2014 its pricing, context window and\ncapabilities as the pricing\ \ source records them. A model hidden for the\ncaller's org answers the same 404 an unknown name does, so a disabled\ \ model\ngets no existence oracle." operationId: get_pricing_model_by_name parameters: - description: 'Name is the model''s name or its slugged id ("zen4", "acme/some-model-1"), matched case-insensitively. It comes from the path: the URL is the addressing authority.' example: zen4 in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: additionalProperties: {} type: object description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one model's catalog entry \u2014 its pricing, context window and capabilities as the pricing source\ \ records them." tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/models: get: description: "Returns the whole model catalog \u2014 every model the gateway serves,\nZen and third-party alike \u2014\ \ filtered to what the caller's org may see. A\nmodel an admin has disabled is absent; one in beta appears only for\ \ an org\ngranted it. A SuperAdmin sees every model, each annotated with its\nenablement state." operationId: get_pricing_models responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.pricingModelList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the whole model catalog \u2014 every model the gateway serves, Zen and third-party alike \u2014 filtered\ \ to what the caller's org may see." tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/paas: get: description: "ListPaaSPlans returns the application-hosting plans \u2014 the deploy-and-host\ntiers, each with its monthly\ \ and annual price, app and memory allowances and\nfeature list." operationId: get_pricing_paas responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.pricingPlanList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "ListPaaSPlans returns the application-hosting plans \u2014 the deploy-and-host tiers, each with its monthly\ \ and annual price, app and memory allowances and feature list." tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/policy: get: description: "Returns the pricing policy document: the revenue-sharing\nterms (the idle-resale share and the open-source\ \ share, each with its\npercentage and who is eligible) and the commitments Hanzo makes about how it\nbills \u2014\ \ no hidden fees, no egress charges, no surprise bills." operationId: get_pricing_policy responses: '200': content: application/json: schema: additionalProperties: {} type: object description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the pricing policy document: the revenue-sharing terms (the idle-resale share and the open-source\ \ share, each with its percentage and who is eligible) and the commitments Hanzo makes about how it bills \u2014 no\ \ hidden fees, no egress charges, no surprise bills." tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/providers: get: description: "Returns the model providers the catalog knows, each with its\ninfo object, filtered to what the caller's\ \ org may see. A provider an admin\nhas disabled is absent \u2014 and so are its models everywhere else on this\n\ surface, because a provider's state cascades to what it serves." operationId: get_pricing_providers responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.pricingProviderList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the model providers the catalog knows, each with its info object, filtered to what the caller's org may see. tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/services: get: description: "Returns the managed-service rate cards \u2014 Search, Crawl,\nVector, Console and Managed Services \u2014\ \ each with its own tiers, and some with\nusage rates or a comparison table. It is the section as authored, un-gated.\n\ \nThese are DISPLAY rate cards: what a product costs, not what a plan grants. No\nentitlement or limit fields ride\ \ here, so nothing can bill off them." operationId: get_pricing_services responses: '200': content: application/json: schema: additionalProperties: {} type: object description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the managed-service rate cards \u2014 Search, Crawl, Vector, Console and Managed Services \u2014 each\ \ with its own tiers, and some with usage rates or a comparison table." tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/subscriptions: get: description: "Returns the API subscription plans \u2014 the account-level\ntiers a customer subscribes to, each with\ \ its monthly and annual price,\nincluded credit, rate limits and feature list." operationId: get_pricing_subscriptions responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.pricingPlanList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the API subscription plans \u2014 the account-level tiers a customer subscribes to, each with its\ \ monthly and annual price, included credit, rate limits and feature list." tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/summary: get: description: "Returns the catalog's headline statistics \u2014 model counts by\nfamily and the provider directory. The\ \ provider sub-object is filtered to what\nthe caller's org may see, so a disabled provider's name never leaks; the\n\ aggregate counts are the catalog's own, over everything it holds." operationId: get_pricing_summary responses: '200': content: application/json: schema: additionalProperties: {} type: object description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the catalog's headline statistics \u2014 model counts by family and the provider directory." tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/sync: post: description: 'Refreshes the third-party section of the catalog from its upstream listings and returns the time the refreshed catalog was stamped with. The fetch runs in Go and the markup transform in the pricing bundle. SuperAdmin only; every other caller is refused.' operationId: post_pricing_sync responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.pricingSyncOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Refreshes the third-party section of the catalog from its upstream listings and returns the time the refreshed catalog was stamped with. tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/tariff: get: description: "Returns the platform's rate card: what a bill is made of, what each\npart costs, and the completion windows\ \ a request may ask for.\n\nFOUR COMPONENTS, and every charge is one of them \u2014 model inference, computer,\nweb\ \ tools and media generation. Two are quoted before they run, so an agent is\nrefused before it breaches its budget;\ \ two are booked from what they used,\nbecause neither a provider's charge nor a render's cost is knowable in advance.\n\ \nEVERY AMOUNT IS INTEGER MICRO-USD (1 USD = 1,000,000), stated once in `unit`,\nand each rate says what one unit\ \ of it is in `per`. The compute rates are per\nHOUR because that is the unit a span is priced in \u2014 rate \xD7\ \ seconds / 3600 \u2014 and\nbecause a GiB-second is four and a half micro-USD, which no integer holds.\n\nThe rates\ \ are the ones the ledger books: each is resolved through the same\nauthority the metering path reads, falling back\ \ to the same compiled floor. A\nrate of zero is a price and not an absence \u2014 a paused computer, a computer's\n\ creation, the interfaces and a seat all cost nothing by design." operationId: get_pricing_tariff responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.Card' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the platform''s rate card: what a bill is made of, what each part costs, and the completion windows a request may ask for.' tags: - pricing x-app: pricing x-public: true x-tool: true /v1/pricing/tools: get: description: "Returns the per-use tool prices \u2014 web search, web fetch, code\ninterpreter, file storage, image generation,\ \ speech-to-text and\ntext-to-speech \u2014 each with the unit it is billed by and its price in that\nunit.\n\nThe\ \ two WEB rows are priced from the rate card rather than from the catalog,\nbecause those are the rows the platform\ \ charges by the call and a published\nnumber that is also a charged one has exactly one home (see tariff.go). Read\n\ them as integer micro-USD at /v1/pricing/tariff; the decimal here is the\ndisplay this list has always carried." operationId: get_pricing_tools responses: '200': content: application/json: schema: $ref: '#/components/schemas/pricing.pricingToolList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the per-use tool prices \u2014 web search, web fetch, code interpreter, file storage, image generation,\ \ speech-to-text and text-to-speech \u2014 each with the unit it is billed by and its price in that unit." tags: - pricing x-app: pricing x-public: true x-tool: true /v1/principal: get: description: "Returns the caller's org as an economic principal, composed from the apps that\nown each fact: its legal\ \ entity and founders' identity verification (company),\nthe W-9 or W-8 it certified (tax), its wallets (wallet),\ \ the agents that answer\nto it \u2014 spawned ones with the agent that spawned them \u2014 (agents), its screening\n\ against the OFAC, UN, EU and UK sanctions lists and the embargoed jurisdictions,\nrun now, and what it still lacks\ \ to pay and be paid. The read changes nothing:\na possible match it finds is recorded for a platform reviewer when\ \ a payment to\nor from the org is cleared.\n\nAny member reads it. The detail behind a verdict \u2014 founders' names\ \ and\nstatuses, masked tax numbers, treaty claims, sanctions matches \u2014 is answered to\nthe org's admins only.\ \ An owning app this deployment does not run is named in\nsources as absent rather than read as empty." operationId: get_principal responses: '200': content: application/json: schema: $ref: '#/components/schemas/principal.View' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the caller's org as an economic principal, composed from the apps that own each fact: its legal entity\ \ and founders' identity verification (company), the W-9 or W-8 it certified (tax), its wallets (wallet), the agents\ \ that answer to it \u2014 spawned ones with the agent that spawned them \u2014 (agents), its screening against the\ \ OFAC, UN, EU and UK sanctions lists and the embargoed jurisdictions, run now, and what it still lacks to pay and\ \ be paid." tags: - principal x-app: principal x-public: true x-tool: true /v1/principal/agents/{ref}: get: description: 'Attests which principal one of the caller''s agents answers to: the org, the chain of agents that spawned it, and the same signed with the platform''s key as a JWS (EdDSA) a verifier checks offline against GET /v1/principal/keys. Any member of the agent''s org. An agent of another org is not found. A spawned agent answers to its parent''s org, which is its own: a parent is resolved only in the org the agent is created in. The statement is valid for 24 hours.' operationId: get_principal_agents_by_ref parameters: - description: Ref is the agent's id or its name in the caller's org. in: path name: ref required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/principal.Attestation' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Attests which principal one of the caller''s agents answers to: the org, the chain of agents that spawned it, and the same signed with the platform''s key as a JWS (EdDSA) a verifier checks offline against GET /v1/principal/keys.' tags: - principal x-app: principal x-public: true x-tool: true /v1/principal/clearance: get: description: 'Lists the caller org''s clearances, newest first, 200 at most; a payee narrows them. Any member of the paying org.' operationId: get_principal_clearance parameters: - description: Payee narrows to one payee. in: query name: payee required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/principal.clearanceList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller org's clearances, newest first, 200 at most; a payee narrows them. tags: - principal x-app: principal x-public: true x-tool: true post: description: "Decides whether the caller's org may pay another org, BEFORE the payment: what\nmust happen first, what\ \ must be withheld and why, what must be reported, by\nwhom and when, and how the payment may settle net of withholding.\ \ Any member of\nthe paying org; the decision is recorded and on the audit trail in one\ntransaction.\n\nThe rules,\ \ each cited on the answer: payer's and payee's founders identity-verified\n(Hanzo policy); payer and payee screened\ \ clear of the OFAC, UN, EU and UK lists\nand the embargoed jurisdictions \u2014 a confirmed match blocks, a possible\ \ one waits\non a Hanzo platform reviewer, and lists that are not loaded and fresh clear\nnobody; the payee's status\ \ is what the form the payee granted the payer says,\nand until it grants one the payee furnishes it; a U.S. payee\ \ with no TIN on\nfile owes its W-9, and paid without one 24% backup withholding applies (IRC\n\xA73406); a foreign\ \ payee on a valid W-8BEN or W-8BEN-E is withheld on at 30% on\nU.S.-source income (IRC \xA71441, \xA71442), reduced\ \ by a treaty rate the form\nclaims; a service performed outside the United States is foreign-source and\nnot withheld\ \ on; Form 1099 by the payer for a reportable U.S. payee at the\nyear's threshold, Forms 1042-S and 1042 for U.S.-source\ \ income to a foreign\nperson, and platform-operator reports (EU DAC7, UK, Canada, Australia) where\nthe payer is\ \ the payee's platform. Every filer named is an org, never Hanzo.\n\nNothing of the payee is answered but what the\ \ payer must know to act: that the\npayment clears, what the payee must furnish, or that it cannot clear and the\n\ payer should contact support \u2014 never why, and never the payee's status before\nthe payee grants its form. A payer\ \ asks about at most 50 new payees a day and\n120 payments an hour." operationId: post_principal_clearance requestBody: content: application/json: example: amount: '5000.00' category: services payee: globex performed: DE schema: $ref: '#/components/schemas/principal.clearIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/principal.Clearance' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Decides whether the caller''s org may pay another org, BEFORE the payment: what must happen first, what must be withheld and why, what must be reported, by whom and when, and how the payment may settle net of withholding.' tags: - principal x-app: principal x-public: true x-tool: true /v1/principal/clearance/{id}: get: description: 'Reads one of the caller org''s clearances, as it was decided. Any member of the paying org; another org''s clearance is not found.' operationId: get_principal_clearance_by_id parameters: - description: ID is the clearance, "clr_"-prefixed. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/principal.Clearance' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads one of the caller org's clearances, as it was decided. tags: - principal x-app: principal x-public: true x-tool: true /v1/principal/keys: get: description: 'Publishes the keys attestations are signed with, as a JSON Web Key Set (RFC 7517; Ed25519 keys as RFC 8037 writes them). Open to anyone: a verifier verifying an agent''s statement fetches this once and verifies offline. A key a rotation retired stays here and signs nothing, so a statement signed before the rotation verifies until it expires.' operationId: get_principal_keys responses: '200': content: application/json: schema: $ref: '#/components/schemas/principal.JWKS' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused security: [] summary: Publishes the keys attestations are signed with, as a JSON Web Key Set (RFC 7517; Ed25519 keys as RFC 8037 writes them). tags: - principal x-app: principal x-public: true x-tool: true post: description: "Rotates the attestation signing key: a new Ed25519 key is generated, sealed in\nKMS beside the others,\ \ and signs every statement from now on; every earlier key\nis retired \u2014 still published, its private half destroyed.\ \ SuperAdmin only, on\nthe audit trail before the new set is written, and followed on the trail by a\ncorrection if\ \ the write does not take. One rotation runs at a time, and one\nthat finds the set rotated under it since it read\ \ it is refused (409). The\nfirst call provisions the deployment's first key." operationId: post_principal_keys responses: '201': content: application/json: schema: $ref: '#/components/schemas/principal.JWKS' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Rotates the attestation signing key: a new Ed25519 key is generated, sealed in KMS beside the others, and\ \ signs every statement from now on; every earlier key is retired \u2014 still published, its private half destroyed." tags: - principal x-app: principal x-public: true x-tool: true /v1/principal/matches: get: description: "Lists sanctions matches across every org, oldest first \u2014 the platform's review\nqueue. SuperAdmin\ \ only: a match names an org the platform screened, and the\norg it names is not the one to decide it.\n\nA match\ \ is one org against one designation or embargoed jurisdiction, with\nevery name, address or country of the org that\ \ brought them close. Open\nmatches are the ones no payment to or from their org clears past until a\nreviewer decides\ \ them; a match is recorded when a payment to or from its org is\ncleared." operationId: get_principal_matches parameters: - description: 'Status is open, cleared or confirmed; open when omitted. "all" lists every match.' in: query name: status required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/principal.matchList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists sanctions matches across every org, oldest first \u2014 the platform's review queue." tags: - principal x-app: principal x-public: true x-tool: true /v1/principal/matches/{id}: post: description: "Decides one sanctions match, as a Hanzo platform reviewer. SuperAdmin only \u2014\nan admin of the org\ \ the match names is refused, because that would be the org\ndeciding its own case \u2014 and the decision is written\ \ and put on the audit trail\nin one transaction: a decision the trail does not take is not made.\n\ncleared records\ \ a namesake: the org's names resemble a designated person's and\nthe other identifiers show a different one. It covers\ \ the hits the reviewer\nread, and a later name close to the same person reopens it. confirmed records\nthe designated\ \ person itself, and blocks every payment to or from the org\nwhatever it is later called. A decision may be changed\ \ only by another\ndecision, with its own note." operationId: post_principal_matches_by_id parameters: - description: ID is the match, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: decision: cleared note: date of birth 1984-02-11 conflicts with the entry's 1961 schema: $ref: '#/components/schemas/principal.decideIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/principal.Match' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Decides one sanctions match, as a Hanzo platform reviewer. tags: - principal x-app: principal x-public: true x-tool: true /v1/principal/statements/{id}: get: description: "Reads whether one statement the platform signed about an agent still stands:\nlive, expired, or revoked\ \ and when. Open to anyone \u2014 a verifier holding a\nTaxPrincipalCredential checks it here after checking its signature\ \ offline\nagainst GET /v1/principal/keys. The answer names nothing the statement itself\ndoes not." operationId: get_principal_statements_by_id parameters: - description: ID is the statement's jti, as its JWS carries it. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/principal.Statement' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused security: [] summary: 'Reads whether one statement the platform signed about an agent still stands: live, expired, or revoked and when.' tags: - principal x-app: principal x-public: true x-tool: true /v1/project: get: description: "Returns every project your org owns.\n\nEach row carries the slug, name, framework, visibility, status\ \ and live URL \u2014\nthe same rows console and the builder render, because there is only one store\nbehind both.\ \ It requires a validated principal (401 without one) and is keyed\nby that principal's org, so it never contains\ \ another tenant's project." operationId: get_project responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/project.projectsProject' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns every project your org owns. tags: - project x-app: project x-public: true x-tool: true post: description: "Creates a project \u2014 the handle a site is deployed and served\nunder \u2014 and answers 201 with it\ \ in `draft`.\n\n`name` is required; `slug` is derived from the name when omitted and is the\nidentifier that matters\ \ \u2014 it becomes the S3 key segment, the public host\n`.hanzo.app`, and the handle every later call addresses,\ \ so it must\nmatch `^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$` and may not be a reserved label\nsuch as `api` or `admin`.\ \ `framework` is a build hint from a closed set,\ndefaulting to `static`; it never gates a deploy, it only tells CI\ \ how to build\na linked repo.\n\nTwo defaults are worth knowing: the analytics beacon is ON unless `analytics`\n\ is explicitly false, and `visibility` is `public` unless asked otherwise.\nPublishing publicly is free; PRIVATE is\ \ the paid feature, and an unfunded org\nasking for it is refused rather than quietly published as public. Creation\n\ also provisions the project's data space and a canonical git repo, both\nbest-effort \u2014 neither can fail the create.\n\ \nScope: a validated principal is required (401 without one) and the project is\ncreated in THAT principal's org.\ \ The slug is unique per org, so a slug already\nused in the caller's own org is a 409 while the same slug in another\ \ org is\nirrelevant." operationId: post_project requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsCreate' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/project.projectsProject' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Creates a project \u2014 the handle a site is deployed and served under \u2014 and answers 201 with it in\ \ `draft`." tags: - project x-app: project x-public: true x-tool: true /v1/project/edge: get: description: 'health reports whether a publish reaches readers, rather than whether it was accepted. Those are different questions and only the second one was ever visible. It asks the edge and nothing else. There is no live call to the provider here: Configured is a local fact, it is the fact that was missing, and a health check that spends a third-party API call is one an operator learns not to run.' operationId: get_project_edge responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.edgeState' description: ok '503': content: application/json: schema: $ref: '#/components/schemas/project.edgeState' description: service unavailable default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: health reports whether a publish reaches readers, rather than whether it was accepted. tags: - project x-app: project x-public: true x-tool: true /v1/project/fork: post: description: "Creates a project seeded from a PUBLISHED EXAMPLE \u2014 either a\nstarter-kit template from the ONE embedded\ \ gallery catalog, or any live\nproject on the platform (an example a seeded creator published, or another\norg's\ \ app serving at .hanzo.app). Answers 201 with the new project.\n\n`slug` names the PARENT to fork and is required.\ \ Templates resolve first, and\nthe caller org's own private templates ahead of the public gallery, so a\ncurated\ \ template slug keeps meaning the same thing even if someone later\npublishes a live project under it; `variant` picks\ \ that template's\nformat/page/theme. If no template matches, the slug resolves to the UNIQUE\nlive project that owns\ \ it across all orgs \u2014 the same resolution the site edge\nuses to serve .hanzo.app, so what you can browse\ \ is what you can fork.\n\n`name` and `target` override the derived project name and slug; everything\nelse is inherited\ \ from the parent. A live parent contributes its REPO, so the\nchild builds from the same source \u2014 the parent's\ \ deployed bytes are never\ncopied, because releases are per-tenant by design and the fork publishes its\nown. The\ \ parent it actually resolved is stamped on the child as `forkedFrom`,\nso attribution is a fact recorded at fork\ \ time rather than a claim\nreconstructed later.\n\nIt funnels through the SAME create path POST /v1/project uses,\ \ so slug\nvalidation, org scoping, ID minting and the 409 on a slug the caller's own org\nalready uses are identical.\n\ \nScope: a validated principal is required (401 without one) and the child is\ncreated in THAT principal's org." operationId: post_project_fork requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsFork' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/project.projectsProject' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Creates a project seeded from a PUBLISHED EXAMPLE \u2014 either a starter-kit template from the ONE embedded\ \ gallery catalog, or any live project on the platform (an example a seeded creator published, or another org's app\ \ serving at .hanzo.app)." tags: - project x-app: project x-public: true x-tool: true /v1/project/sites: get: description: 'Returns the org''s deployed sites at the pretty URLs they serve at. It reads the SAME org-scoped store as /v1/project and keeps only the projects that are actually `live`, so a draft or a failed build is not advertised as a site. Scope: a validated principal is required (401 without one) and the list is keyed by that principal''s org.' operationId: get_project_sites responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/project.projectsSite' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the org's deployed sites at the pretty URLs they serve at. tags: - project x-app: project x-public: true x-tool: true post: description: "Generates a self-contained, mobile-responsive static site from a\nnatural-language brief and deploys it\ \ live in one call.\n\nOne inference call turns `brief` (capped at 8 KiB) into a file manifest, which\nthen runs through\ \ the SAME validation, guards and viewport guarantee as a\nhand-supplied manifest: index.html required at the root,\ \ absolute and\ntraversal paths rejected, per-file and total size capped, and a mobile\nviewport meta tag injected\ \ into every HTML document that lacks one. The\ngenerated site is fully inline \u2014 no CDNs, no remote fonts or\ \ images \u2014 so it is\nCSP-safe. `slug` and `name` are optional: the model's own title is preferred,\nand a slug\ \ is derived or minted when none is given.\n\nIt writes into the SAME org-scoped store as /v1/project \u2014 it ensures\ \ a\nproject (framework `static`) for the resolved slug and records a deployment \u2014\nso this is a second entry\ \ point to one publish pipeline, not a second copy of\nproject state. Ordering is the billing contract: the hosting\ \ gate runs BEFORE\nany inference or upload, so a denied gate generates and uploads NOTHING, and\nthe debit lands\ \ once, only after the site is actually live. The tokens are\nbilled to the same ledger the hosting fee was reserved\ \ against.\n\nAnswers 503 when object storage or inference is unconfigured, and 400 when the\nmodel's manifest cannot\ \ be parsed or fails the guards.\n\nScope: a validated principal is required (401 without one) and the site is\npublished\ \ into THAT principal's org." operationId: post_project_sites requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsBuildSite' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsSiteDeploy' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Generates a self-contained, mobile-responsive static site from a natural-language brief and deploys it live in one call. tags: - project x-app: project x-public: true x-tool: true /v1/project/sites/deploy: post: description: "Deploys a caller-supplied file manifest \u2014 the deploy_site\ncapability an agent calls \u2014 and answers\ \ with where it went live.\n\n`files` is a list of {path, content} pairs, the same shape the brief build\nemits, and\ \ it runs through the SAME guards: index.html required at the root,\nabsolute and traversal paths rejected, per-file\ \ and total size capped, and a\nmobile viewport meta tag injected into every HTML document that lacks one \u2014 so\n\ a hand-built site is exactly as safe and as responsive as a generated one.\n`slug` and `name` are optional; a slug\ \ is derived from the name or minted.\n\nIt writes into the SAME org-scoped store as /v1/project, ensuring a project\n\ (framework `static`) for the resolved slug and recording a deployment. The\nhosting gate runs before the upload and\ \ the debit lands once, after the site\nis live \u2014 a failed upload is never billed. Answers 503 when object storage\ \ is\nunconfigured.\n\nScope: a validated principal is required (401 without one) and the site is\npublished into\ \ THAT principal's org." operationId: post_project_sites_deploy requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsDeploySite' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsSiteDeploy' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Deploys a caller-supplied file manifest \u2014 the deploy_site capability an agent calls \u2014 and answers\ \ with where it went live." tags: - project x-app: project x-public: true x-tool: true /v1/project/sites/{slug}: get: description: "Returns one site \u2014 the same row ListSites carries, for one slug.\n\nEvery sub-resource under a site\ \ already answered: deployments, releases,\npublish. The site itself did not, and a route that is never registered\n\ answers 404 for a LIVE site exactly as it does for one that was never\ncreated. So the one call a client makes to\ \ ask \"is it there yet?\" could only\never say no, and a CI lane watching for its own publish would wait forever\ \ on\na success it had already achieved.\n\nThe org is the caller's, never a path segment. A slug is unique within\ \ an org\nand two orgs may both own `tel`; taking the org from the validated principal\ninstead of the URL means a\ \ caller cannot read another org's site by editing a\npath, and it is the same scope ListProjects and ListSites already\ \ use.\n\nA site that exists but is not live is NOT found here, matching ListSites,\nwhich keeps only `live` rows\ \ so a draft or a failed build is never advertised\nas a site. One definition of \"is a site\", used by both." operationId: get_project_sites_by_slug parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsSite' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one site \u2014 the same row ListSites carries, for one slug." tags: - project x-app: project x-public: true x-tool: true /v1/project/tags: get: description: "Returns the client-side pixels the SITE has connected (GA4, Google Ads, LinkedIn, Meta, Pinterest, Reddit,\ \ TikTok, X) with their NON-SECRET ids, so the hosted tag injects them first-party and stamps each browser event with\ \ the same event_id the server-side Conversions API uses \u2014 deduping the two. Resolved per site: by the publishable\ \ key on ?key= when it names a project, else by the request host, so hanzo.ai and hanzo.chat carry different tags\ \ under one org. WITHOUT a resolvable site it answers an empty set at 200 \u2014 a page never breaks on its tag config." operationId: get_project_tags responses: 2XX: content: application/json: schema: $ref: '#/components/schemas/tagConfig' description: Success summary: "The site's browser tag set for the hosted tag \u2014 which pixels to inject, by publishable key" tags: - project x-app: project x-public: true /v1/project/{slug}: delete: description: "Deletes a project and takes its site off the internet.\n\nThe metadata delete is authoritative and everything\ \ after it is best-effort,\nin this order: the public `` subdomain binding is released so the slug is\nfree\ \ to reclaim, the release rows are dropped so a reclaimed slug never\ninherits the previous owner's rollback menu,\ \ the git source is retired on\nevery copy it has so a reclaimed slug never adopts a repository left behind\n(visibility.go),\ \ the S3 origin is purged under BOTH `//` and the\nsite's sibling release space, and the edge cache-tag\ \ is flushed. A failure in\nany of those is logged and the delete still answers 204 \u2014 resurrecting a\nproject\ \ because a purge missed would be worse than a leaked prefix.\n\nScope: a validated principal is required (401 without\ \ one) and the project is\nresolved within that principal's org, so another tenant's slug is a 404 and\nnothing of\ \ theirs is touched." operationId: delete_project_by_slug parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes a project and takes its site off the internet. tags: - project x-app: project x-public: true x-tool: true get: description: "Returns one project of yours by slug \u2014 its settings, its live URL\nand the deployment currently serving\ \ it.\n\nScope: a validated principal is required (401 without one) and the lookup is\nkeyed by (org, slug), so another\ \ tenant's slug is a 404 exactly like a\nnonexistent one." operationId: get_project_by_slug parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsProject' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one project of yours by slug \u2014 its settings, its live URL and the deployment currently serving\ \ it." tags: - project x-app: project x-public: true x-tool: true patch: description: "Changes a project's settings, and only the settings you send.\n\nEvery field is optional and absent means\ \ \"leave it\": `name` may not be blanked,\n`framework` must stay a known build hint, and `cacheControl` is capped\ \ at 256\ncharacters with no newlines (it becomes a response header). `visibility` flips\npublic/private under the\ \ same rule as create \u2014 public is free, private needs a\nfunded org. `upstream` and `license` are free-text credit\ \ for third-party work,\nand sending \"\" clears one. Changing anything reconciles the project's canonical\ngit repo,\ \ so a visibility change reaches the source and not just the listing.\n\n`hidden`/`hiddenReason` are platform MODERATION\ \ and are ignored unless the\ncaller is a platform admin; they remove a project from the public catalogue\nwithout\ \ touching the publisher's own visibility choice, so un-hiding restores\nexactly what they asked for.\n\nScope: a\ \ validated principal is required (401 without one) and the project is\nresolved within that principal's org, so another\ \ tenant's slug is a 404." operationId: patch_project_by_slug parameters: - description: "Slug is the project to update, from the path. The URL is the addressing\nauthority \u2014 a `slug` in\ \ the body cannot move the write to another project." in: path name: slug required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsUpdate' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsProject' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes a project's settings, and only the settings you send. tags: - project x-app: project x-public: true x-tool: true /v1/project/{slug}/deploy: post: description: "Takes a built site live at `https://.hanzo.app` in one call. The body is the site itself \u2014\ \ a `zip` or `tar.gz` holding `index.html` at its root (or a single wrapper directory that does), sent raw or as a\ \ multipart file part. It is unpacked to the site's own storage prefix and served immediately, answering the finished\ \ deployment.\n\nIt is bounded by the edge body limit (16 MiB by default), and that bound is the whole reason the\ \ other path exists: an oversized POST is refused by the server BEFORE any handler runs and surfaces as an opaque\ \ `400 Error when parsing request` that reads like a malformed payload rather than a size cap. A site too large for\ \ one archive opens a deployment with `POST /v1/project/{slug}/deployments` instead and writes its files straight\ \ to storage against the scoped grant that answers with \u2014 no body limit, and no bytes through this API at all.\n\ \nBilling is fail-closed and fails FIRST: the hosting gate runs before anything is parsed or uploaded, so an unfunded\ \ org is 402 and an unreachable commerce is 503 with nothing written. The debit lands only on success \u2014 a failed\ \ upload is never billed and never flips the live site \u2014 and a redeploy answers the SAME URL, because slug and\ \ apex are stable.\n\nScope: a validated principal is required (401 without one) and the site is resolved within that\ \ principal's org, so another tenant's slug is a 404. Object storage must be configured (503); an archive that does\ \ not walk is a 400 and one over the size cap is a 413." operationId: post_project_by_slug_deploy parameters: - in: path name: slug required: true schema: type: string requestBody: content: application/octet-stream: schema: format: binary type: string responses: 2XX: content: application/json: schema: $ref: '#/components/schemas/projectsDeployment' description: Success summary: Upload a built site as one archive and serve it tags: - project x-app: project x-public: true /v1/project/{slug}/deployments: get: description: "Returns a project's deploy history, newest version first.\n\nEvery deploy of the project is a row \u2014\ \ uploads, generated sites, and git/CI\nbuilds alike \u2014 carrying its version, status, source, commit, live URL,\ \ file\ncount and byte count. The short-lived upload grant a queued git deployment was\nhanded is NOT replayed here:\ \ it exists only on the 202 that minted it, so a\ngrant cannot outlive its build by being fetched again.\n\nScope:\ \ a validated principal is required (401 without one) and the project is\nresolved within that principal's org, so\ \ another tenant's slug is a 404." operationId: get_project_by_slug_deployments parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/project.projectsDeployment' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns a project's deploy history, newest version first. tags: - project x-app: project x-public: true x-tool: true post: description: "Opens a deployment and hands back a short-lived, prefix-scoped\ngrant to write its bytes straight to object\ \ storage. Answers 202.\n\nThis is the path for a site too large to send as one archive: a real export is\nhundreds\ \ of megabytes against a 16 MiB body limit, so the bytes deliberately do\nNOT pass through the API. The answer carries\ \ `bucket`, `prefix` and `upload` \u2014\na presigned POST policy that S3 itself confines to this site's prefix\n\ (starts-with `//`), expires in 30 minutes and bounds each object.\nSo a build writes its own files and\ \ holds no standing bucket credential; there\nis nothing to rotate and nothing that leaks between tenants. Never guess\ \ the\nprefix \u2014 it is server-derived, and a guessed one lands where nothing is served.\n\nThe deployment is `queued`\ \ until POST .../deployments/{id}/complete flips it\nlive (or error). That completion is also where DELETION happens:\ \ the grant\nauthorizes writes only, so a build cannot remove a file, and cloud reconciles\nthe prefix against the\ \ `keys` manifest the completion carries. A build that\ndies before completing leaves the deployment queued rather\ \ than a half-live\nsite.\n\nThe grant is on the 202 and NOWHERE else \u2014 it is never stored and never\nreplayed\ \ on a later read, so it cannot outlive the build it was minted for. A\ndeployment whose grant could not be minted\ \ is still created and still\ncompletable; it simply carries no `upload`, and a caller with no other way to\nwrite\ \ should treat that as the failure it is.\n\nBilling: the hosting gate runs BEFORE anything is created (402 unfunded,\ \ 503\ncommerce unreachable), and the debit lands on the completion that goes live \u2014\nnever on a queued or failed\ \ build.\n\nScope: a validated principal is required (401 without one) and the site is\nresolved within that principal's\ \ org, so another tenant's slug is a 404." operationId: post_project_by_slug_deployments parameters: - description: Slug is the site to deploy, from the path. in: path name: slug required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsDeployStart' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/project.projectsDeployment' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Opens a deployment and hands back a short-lived, prefix-scoped grant to write its bytes straight to object storage. tags: - project x-app: project x-public: true x-tool: true /v1/project/{slug}/deployments/{id}: get: description: "Returns one deployment of a project by id.\n\nIt is how a console follows a build: the status (`queued`,\ \ `uploading`,\n`live`, `error`), the message a failure left, and the URL and prefix it went\nlive at. Like the history,\ \ it never replays the upload grant.\n\nScope: a validated principal is required (401 without one). Both the project\n\ and the deployment are resolved within that principal's org, so a deployment\nof another project \u2014 or of another\ \ tenant \u2014 is a 404." operationId: get_project_by_slug_deployments_by_id parameters: - description: Slug is the project the deployment belongs to, from the path. in: path name: slug required: true schema: type: string - description: "ID is the deployment id, from the path. A deployment of another project \u2014\nor of another tenant's\ \ project \u2014 is not found." in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsDeployment' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one deployment of a project by id. tags: - project x-app: project x-public: true x-tool: true /v1/project/{slug}/deployments/{id}/complete: post: description: "CompleteDeployment is the CI completion hook that flips a queued git\ndeployment to live (or error) once\ \ CI has synced the built site to S3.\n\n`status` must be `live` or `error`. On a LIVE completion the public host\ \ is\nclaimed FIRST, so the deployment reports the URL it actually OWNS \u2014 a\nCI-supplied `liveUrl` is a hint\ \ that can refine that URL but can never assert\na subdomain another tenant holds. `keys` is the manifest CI just\ \ uploaded,\nrelative to the deployment prefix: cloud reconciles the prefix against it so a\npage deleted from the\ \ build actually stops serving. Omit `keys` and nothing is\ndeleted \u2014 the prefix only grows. Reconciliation runs\ \ only on a live completion\n(pruning against a failed build's manifest would delete the site the last good\nbuild\ \ is still serving) and is best-effort, so a stale leftover never turns a\nsuccessful deploy into a 500. A live completion\ \ is also the one billable\nmoment on the git path; an error completion bills nothing.\n\nScope: a validated principal\ \ is required (401 without one). CI authenticates\nwith an org-scoped token through the gateway, so the deployment\ \ is resolved\nwithin that principal's org and another tenant's slug or deployment id is a\n404." operationId: post_project_by_slug_deployments_by_id_complete parameters: - description: Slug is the project the deployment belongs to, from the path. in: path name: slug required: true schema: type: string - description: ID is the queued deployment to complete, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsComplete' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsDeployment' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: CompleteDeployment is the CI completion hook that flips a queued git deployment to live (or error) once CI has synced the built site to S3. tags: - project x-app: project x-public: true x-tool: true /v1/project/{slug}/domains: get: description: "Returns every custom hostname this site holds: the live ones, plus\nany pending claim with the DNS records\ \ it still owes.\n\n`domains` is the routing answer \u2014 the hosts that are verified right now \u2014\nwhile `claims`\ \ is the full panel, one row per host, each saying whether it is\nlive or pending and, if pending, exactly what to\ \ publish.\n\nScope: a validated principal is required (401 without one) and the site is\nresolved within that principal's\ \ org, so another tenant's slug is a 404." operationId: get_project_by_slug_domains parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsDomains' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns every custom hostname this site holds: the live ones, plus any pending claim with the DNS records it still owes.' tags: - project x-app: project x-public: true x-tool: true post: description: "Attaches one or more CUSTOM public hostnames to this org's site.\n\nBinding a host you do not own would\ \ let you shadow it at the edge, so which\noutcome you get depends on whether ownership is already established: a\ \ SuperAdmin\nvouches (the operator manages the customer's DNS, so its bind IS the proof) and\nbinds VERIFIED immediately;\ \ every other caller, INCLUDING an admin of the\ndeployment's own brand org, has the host CLAIMED as pending and gets\ \ the DNS\nchallenge back in `bound[].records`. A pending claim HOLDS the name so nobody\nelse can take it, but it\ \ does not route until POST .../domains/{host}/verify\nproves control.\n\nA hostname we operate is refused to a non-vouched\ \ caller (those are assigned\nby the platform, never claimed), a host another site already holds is a 409,\nand a\ \ name the platform holds is a 400 for EVERY caller \u2014 a vouch skips the\nownership proof, never the host table's\ \ own invariant. Claims and binds are\nidempotent for the same\n(org, slug), and re-claiming returns the SAME token\ \ rather than invalidating a\nrecord the customer has already published. The edge cache-tag is flushed\nafterwards\ \ so a newly-verified host serves the current build immediately.\n\nScope: a validated principal is required (401\ \ without one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404." operationId: post_project_by_slug_domains parameters: - description: Slug is the site the hosts attach to, from the path. in: path name: slug required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsDomainsBind' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsBoundDomains' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Attaches one or more CUSTOM public hostnames to this org's site. tags: - project x-app: project x-public: true x-tool: true /v1/project/{slug}/domains/{host}: delete: description: 'Gives a custom hostname back, so the name is free to reuse. A claim is FIRST-COME and global, so an add-only surface was not ownership but a leak: a customer who mistyped a domain, or claimed one they later moved elsewhere, could neither reuse it nor let anyone else. This is the third writer that closes it. The release is scoped to (host, org, slug), so it can only ever drop THIS tenant''s own claim, and it is IDEMPOTENT: releasing a host we do not hold is a clean 204, never a 404 that would let a caller probe which hosts other tenants hold. The edge cache-tag is flushed, since the host stops routing here. Scope: a validated principal is required (401 without one) and the site is resolved within that principal''s org, so another tenant''s slug is a 404.' operationId: delete_project_by_slug_domains_by_host parameters: - description: Slug is the project the host is attached to, from the path. in: path name: slug required: true schema: type: string - description: 'Host is the custom hostname, from the path. It is cleaned to its canonical form (lowercased, trailing dot dropped) before anything is looked up.' in: path name: host required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Gives a custom hostname back, so the name is free to reuse. tags: - project x-app: project x-public: true x-tool: true /v1/project/{slug}/domains/{host}/verify: post: description: "Checks the DNS challenge for a pending custom hostname and, when\nit passes, promotes the host so it begins\ \ routing at the edge.\n\nIt answers 200 either way, with the host's honest current state: verified once\nthe TXT\ \ record is found, still pending \u2014 with the records to publish and the\nresolver's own explanation in `detail`\ \ \u2014 when it is not. A not-yet is not an\nerror: the check ran, DNS simply has not propagated, and the customer\ \ retries.\nAn already-verified host is returned unchanged without re-resolving. On a\nsuccessful promotion the edge\ \ cache-tag is flushed, since the host routes as\nof that moment.\n\nScope: a validated principal is required (401\ \ without one). Both the site and\nthe claim are resolved within that principal's org, so a host claimed by\nanother\ \ tenant is \"not claimed by this site\"." operationId: post_project_by_slug_domains_by_host_verify parameters: - description: Slug is the project the host is attached to, from the path. in: path name: slug required: true schema: type: string - description: 'Host is the custom hostname, from the path. It is cleaned to its canonical form (lowercased, trailing dot dropped) before anything is looked up.' in: path name: host required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsDomain' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Checks the DNS challenge for a pending custom hostname and, when it passes, promotes the host so it begins routing at the edge. tags: - project x-app: project x-public: true x-tool: true /v1/project/{slug}/publish: post: description: "Promotes a build output into a new release AND goes live with it \u2014\ncreate+activate in one call,\ \ which is the 99% path.\n\nIt is exactly the two halves in sequence with no extra semantics, so the\nstaged flow\ \ and the one-shot flow can never drift apart: `source` is promoted\nunder the same org-relative rule and the same\ \ guards CreateRelease applies,\nthen the site's pointer is flipped to it, the public host is claimed and the\nedge\ \ is purged. Idempotent on unchanged bytes \u2014 same manifest, same release id,\nno copy \u2014 and billed once,\ \ after the release exists.\n\nScope: a validated principal is required (401 without one) and the site is\nresolved\ \ within that principal's org, so another tenant's slug is a 404." operationId: post_project_by_slug_publish parameters: - description: Slug is the site to publish, from the path. in: path name: slug required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsPublish' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsRelease' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Promotes a build output into a new release AND goes live with it \u2014 create+activate in one call, which\ \ is the 99% path." tags: - project x-app: project x-public: true x-tool: true /v1/project/{slug}/purge: post: description: "Flushes the site's edge cache without redeploying anything.\n\nIt invalidates the edge cache-tag `site--`\ \ and stamps `lastPurgeAt`\n(unix seconds), and it NEVER writes or deletes the S3 origin \u2014 the live build\nkeeps\ \ serving; only stale copies held at the edge drop, so the next request\nre-fetches the current artifact from origin.\ \ Idempotent, and an edge that is\nunconfigured or failing is not fatal: `lastPurgeAt` is still stamped and the\n\ answer is still the updated project.\n\nScope: a validated principal is required (401 without one) and the project\ \ is\nresolved within that principal's org, so another tenant's slug is a 404." operationId: post_project_by_slug_purge parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsProject' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Flushes the site's edge cache without redeploying anything. tags: - project x-app: project x-public: true x-tool: true /v1/project/{slug}/releases: get: description: "Returns a site's releases newest-first, marking the active one \u2014\nthe rollback menu.\n\nEach row\ \ carries the release id to activate, the source it was promoted from,\nits object and byte counts, and the URL if\ \ it is the one serving. Retention\nbounds the list, so it is the set that can actually still be rolled back to,\n\ not a full history.\n\nScope: a validated principal is required (401 without one) and the site is\nresolved within\ \ that principal's org, so another tenant's slug is a 404." operationId: get_project_by_slug_releases parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/project.projectsRelease' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns a site's releases newest-first, marking the active one \u2014 the rollback menu." tags: - project x-app: project x-public: true x-tool: true post: description: "Promotes a build output into a new immutable release WITHOUT\nserving it \u2014 the staged half of publishing,\ \ for when you want to check a\nrelease before it goes live. Answers 201.\n\n`source` is a path RELATIVE to your org's\ \ own storage space: the org segment\nis prepended server-side from the validated principal and the bucket is never\n\ in the request at all, so a server-side copy can only ever reach bytes your\norg already owns. The prefix is listed,\ \ content-addressed (SHA-256 over the\nsorted manifest of key/size/etag), and copied into an immutable\n`/.releases///`\ \ prefix; the row is written LAST, so a partial\ncopy is unreachable rather than merely unlikely. Re-publishing an\ \ unchanged\nsource is idempotent BY CONSTRUCTION \u2014 same bytes, same id, no copy at all.\n\nThe source must contain\ \ index.html at its root and stay under the same file\nand byte caps an artifact deploy does (413 past them); a source\ \ that changes\nmid-copy is a 409 and the release is abandoned. Each publish also reclaims\nreleases past the retention\ \ depth, so a site's release space stays bounded.\nThis is the billable half \u2014 the hosting gate runs before any\ \ copy, and the\ndebit lands once the release exists.\n\nScope: a validated principal is required (401 without one)\ \ and the site is\nresolved within that principal's org, so another tenant's slug is a 404." operationId: post_project_by_slug_releases parameters: - description: Slug is the site to publish, from the path. in: path name: slug required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsPublish' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/project.projectsRelease' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Promotes a build output into a new immutable release WITHOUT serving it \u2014 the staged half of publishing,\ \ for when you want to check a release before it goes live." tags: - project x-app: project x-public: true x-tool: true /v1/project/{slug}/releases/{release}/activate: post: description: "Points the site at an existing release \u2014 the go-live, and\nequally the ROLLBACK.\n\nAim it at an\ \ older release and the site serves that one again: releases are\nimmutable and retained to the retention depth, so\ \ nothing is rebuilt or\nre-copied and the flip is one atomic statement. Before the flip, two\nconditions run in the\ \ order that gives each its own honest answer \u2014 the ROW\nsays whether this release exists for this tenant at\ \ all (404, with no signal\nabout a foreign id), and only then do the BYTES say whether it can still serve\n(410 GONE\ \ when retention has reclaimed them; that rollback target is not\ncoming back, so publish again). Going live also\ \ claims the public host and\npurges the edge, so the release is reachable and no cached predecessor is\nserved. NOT\ \ billed: no new content is produced, only a pointer moved.\n\nScope: a validated principal is required (401 without\ \ one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404." operationId: post_project_by_slug_releases_by_release_activate parameters: - description: Slug is the site the release belongs to, from the path. in: path name: slug required: true schema: type: string - description: 'Release is the content-addressed release id ("rel_" + 32 hex chars), from the path. Anything that is not that shape is not found, rather than being interpolated into a storage prefix.' in: path name: release required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsRelease' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Points the site at an existing release \u2014 the go-live, and equally the ROLLBACK." tags: - project x-app: project x-public: true x-tool: true /v1/project/{slug}/shot: get: description: "Returns a screenshot of what this project currently serves, as image/png. The capture is keyed by the\ \ deployment, so a redeploy invalidates it by construction rather than by anyone remembering to clear a cache. A project\ \ with nothing deployed answers 404 \u2014 that is a 404 about the PICTURE and not about the project, which is still\ \ right there in the list. Scoped to the caller's org: a validated principal is required, and a slug belonging to\ \ another org is not found rather than forbidden." operationId: get_project_by_slug_shot parameters: - in: path name: slug required: true schema: type: string summary: Get a PNG of the project's live site tags: - project x-app: project x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/project/{slug}/star: delete: description: "Removes the caller's own bookmark from a project, and answers whether\nit is starred afterwards.\n\nIt\ \ removes only YOUR star \u2014 the same one star wrote \u2014 so a project other\npeople have starred stays on their\ \ lists. Unstarring one you had not starred\nis not an error; it leaves it unstarred." operationId: delete_project_by_slug_star parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsStar' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes the caller's own bookmark from a project, and answers whether it is starred afterwards. tags: - project x-app: project x-public: true x-tool: true put: description: 'Bookmarks a project for the person calling, and answers whether it is starred afterwards. The star is YOURS: it is keyed by you as well as by the project, so two people see two answers for the same one and starring it says nothing about anybody else''s list. Starring a project you have already starred leaves it starred.' operationId: put_project_by_slug_star parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsStar' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Bookmarks a project for the person calling, and answers whether it is starred afterwards. tags: - project x-app: project x-public: true x-tool: true /v1/projects: get: description: "Returns every project your org owns.\n\nEach row carries the slug, name, framework, visibility, status\ \ and live URL \u2014\nthe same rows console and the builder render, because there is only one store\nbehind both.\ \ It requires a validated principal (401 without one) and is keyed\nby that principal's org, so it never contains\ \ another tenant's project." operationId: get_projects responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/project.projectsProject' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns every project your org owns. tags: - project x-app: project x-public: true x-tool: true post: description: "Creates a project \u2014 the handle a site is deployed and served\nunder \u2014 and answers 201 with it\ \ in `draft`.\n\n`name` is required; `slug` is derived from the name when omitted and is the\nidentifier that matters\ \ \u2014 it becomes the S3 key segment, the public host\n`.hanzo.app`, and the handle every later call addresses,\ \ so it must\nmatch `^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$` and may not be a reserved label\nsuch as `api` or `admin`.\ \ `framework` is a build hint from a closed set,\ndefaulting to `static`; it never gates a deploy, it only tells CI\ \ how to build\na linked repo.\n\nTwo defaults are worth knowing: the analytics beacon is ON unless `analytics`\n\ is explicitly false, and `visibility` is `public` unless asked otherwise.\nPublishing publicly is free; PRIVATE is\ \ the paid feature, and an unfunded org\nasking for it is refused rather than quietly published as public. Creation\n\ also provisions the project's data space and a canonical git repo, both\nbest-effort \u2014 neither can fail the create.\n\ \nScope: a validated principal is required (401 without one) and the project is\ncreated in THAT principal's org.\ \ The slug is unique per org, so a slug already\nused in the caller's own org is a 409 while the same slug in another\ \ org is\nirrelevant." operationId: post_projects requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsCreate' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/project.projectsProject' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Creates a project \u2014 the handle a site is deployed and served under \u2014 and answers 201 with it in\ \ `draft`." tags: - project x-app: project x-public: true x-tool: true /v1/projects/edge: get: description: 'health reports whether a publish reaches readers, rather than whether it was accepted. Those are different questions and only the second one was ever visible. It asks the edge and nothing else. There is no live call to the provider here: Configured is a local fact, it is the fact that was missing, and a health check that spends a third-party API call is one an operator learns not to run.' operationId: get_projects_edge responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.edgeState' description: ok '503': content: application/json: schema: $ref: '#/components/schemas/project.edgeState' description: service unavailable default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: health reports whether a publish reaches readers, rather than whether it was accepted. tags: - project x-app: project x-public: true x-tool: true /v1/projects/fork: post: description: "Creates a project seeded from a PUBLISHED EXAMPLE \u2014 either a\nstarter-kit template from the ONE embedded\ \ gallery catalog, or any live\nproject on the platform (an example a seeded creator published, or another\norg's\ \ app serving at .hanzo.app). Answers 201 with the new project.\n\n`slug` names the PARENT to fork and is required.\ \ Templates resolve first, and\nthe caller org's own private templates ahead of the public gallery, so a\ncurated\ \ template slug keeps meaning the same thing even if someone later\npublishes a live project under it; `variant` picks\ \ that template's\nformat/page/theme. If no template matches, the slug resolves to the UNIQUE\nlive project that owns\ \ it across all orgs \u2014 the same resolution the site edge\nuses to serve .hanzo.app, so what you can browse\ \ is what you can fork.\n\n`name` and `target` override the derived project name and slug; everything\nelse is inherited\ \ from the parent. A live parent contributes its REPO, so the\nchild builds from the same source \u2014 the parent's\ \ deployed bytes are never\ncopied, because releases are per-tenant by design and the fork publishes its\nown. The\ \ parent it actually resolved is stamped on the child as `forkedFrom`,\nso attribution is a fact recorded at fork\ \ time rather than a claim\nreconstructed later.\n\nIt funnels through the SAME create path POST /v1/project uses,\ \ so slug\nvalidation, org scoping, ID minting and the 409 on a slug the caller's own org\nalready uses are identical.\n\ \nScope: a validated principal is required (401 without one) and the child is\ncreated in THAT principal's org." operationId: post_projects_fork requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsFork' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/project.projectsProject' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Creates a project seeded from a PUBLISHED EXAMPLE \u2014 either a starter-kit template from the ONE embedded\ \ gallery catalog, or any live project on the platform (an example a seeded creator published, or another org's app\ \ serving at .hanzo.app)." tags: - project x-app: project x-public: true x-tool: true /v1/projects/sites: get: description: 'Returns the org''s deployed sites at the pretty URLs they serve at. It reads the SAME org-scoped store as /v1/project and keeps only the projects that are actually `live`, so a draft or a failed build is not advertised as a site. Scope: a validated principal is required (401 without one) and the list is keyed by that principal''s org.' operationId: get_projects_sites responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/project.projectsSite' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the org's deployed sites at the pretty URLs they serve at. tags: - project x-app: project x-public: true x-tool: true post: description: "Generates a self-contained, mobile-responsive static site from a\nnatural-language brief and deploys it\ \ live in one call.\n\nOne inference call turns `brief` (capped at 8 KiB) into a file manifest, which\nthen runs through\ \ the SAME validation, guards and viewport guarantee as a\nhand-supplied manifest: index.html required at the root,\ \ absolute and\ntraversal paths rejected, per-file and total size capped, and a mobile\nviewport meta tag injected\ \ into every HTML document that lacks one. The\ngenerated site is fully inline \u2014 no CDNs, no remote fonts or\ \ images \u2014 so it is\nCSP-safe. `slug` and `name` are optional: the model's own title is preferred,\nand a slug\ \ is derived or minted when none is given.\n\nIt writes into the SAME org-scoped store as /v1/project \u2014 it ensures\ \ a\nproject (framework `static`) for the resolved slug and records a deployment \u2014\nso this is a second entry\ \ point to one publish pipeline, not a second copy of\nproject state. Ordering is the billing contract: the hosting\ \ gate runs BEFORE\nany inference or upload, so a denied gate generates and uploads NOTHING, and\nthe debit lands\ \ once, only after the site is actually live. The tokens are\nbilled to the same ledger the hosting fee was reserved\ \ against.\n\nAnswers 503 when object storage or inference is unconfigured, and 400 when the\nmodel's manifest cannot\ \ be parsed or fails the guards.\n\nScope: a validated principal is required (401 without one) and the site is\npublished\ \ into THAT principal's org." operationId: post_projects_sites requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsBuildSite' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsSiteDeploy' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Generates a self-contained, mobile-responsive static site from a natural-language brief and deploys it live in one call. tags: - project x-app: project x-public: true x-tool: true /v1/projects/sites/deploy: post: description: "Deploys a caller-supplied file manifest \u2014 the deploy_site\ncapability an agent calls \u2014 and answers\ \ with where it went live.\n\n`files` is a list of {path, content} pairs, the same shape the brief build\nemits, and\ \ it runs through the SAME guards: index.html required at the root,\nabsolute and traversal paths rejected, per-file\ \ and total size capped, and a\nmobile viewport meta tag injected into every HTML document that lacks one \u2014 so\n\ a hand-built site is exactly as safe and as responsive as a generated one.\n`slug` and `name` are optional; a slug\ \ is derived from the name or minted.\n\nIt writes into the SAME org-scoped store as /v1/project, ensuring a project\n\ (framework `static`) for the resolved slug and recording a deployment. The\nhosting gate runs before the upload and\ \ the debit lands once, after the site\nis live \u2014 a failed upload is never billed. Answers 503 when object storage\ \ is\nunconfigured.\n\nScope: a validated principal is required (401 without one) and the site is\npublished into\ \ THAT principal's org." operationId: post_projects_sites_deploy requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsDeploySite' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsSiteDeploy' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Deploys a caller-supplied file manifest \u2014 the deploy_site capability an agent calls \u2014 and answers\ \ with where it went live." tags: - project x-app: project x-public: true x-tool: true /v1/projects/sites/{slug}: get: description: "Returns one site \u2014 the same row ListSites carries, for one slug.\n\nEvery sub-resource under a site\ \ already answered: deployments, releases,\npublish. The site itself did not, and a route that is never registered\n\ answers 404 for a LIVE site exactly as it does for one that was never\ncreated. So the one call a client makes to\ \ ask \"is it there yet?\" could only\never say no, and a CI lane watching for its own publish would wait forever\ \ on\na success it had already achieved.\n\nThe org is the caller's, never a path segment. A slug is unique within\ \ an org\nand two orgs may both own `tel`; taking the org from the validated principal\ninstead of the URL means a\ \ caller cannot read another org's site by editing a\npath, and it is the same scope ListProjects and ListSites already\ \ use.\n\nA site that exists but is not live is NOT found here, matching ListSites,\nwhich keeps only `live` rows\ \ so a draft or a failed build is never advertised\nas a site. One definition of \"is a site\", used by both." operationId: get_projects_sites_by_slug parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsSite' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one site \u2014 the same row ListSites carries, for one slug." tags: - project x-app: project x-public: true x-tool: true /v1/projects/{slug}: delete: description: "Deletes a project and takes its site off the internet.\n\nThe metadata delete is authoritative and everything\ \ after it is best-effort,\nin this order: the public `` subdomain binding is released so the slug is\nfree\ \ to reclaim, the release rows are dropped so a reclaimed slug never\ninherits the previous owner's rollback menu,\ \ the git source is retired on\nevery copy it has so a reclaimed slug never adopts a repository left behind\n(visibility.go),\ \ the S3 origin is purged under BOTH `//` and the\nsite's sibling release space, and the edge cache-tag\ \ is flushed. A failure in\nany of those is logged and the delete still answers 204 \u2014 resurrecting a\nproject\ \ because a purge missed would be worse than a leaked prefix.\n\nScope: a validated principal is required (401 without\ \ one) and the project is\nresolved within that principal's org, so another tenant's slug is a 404 and\nnothing of\ \ theirs is touched." operationId: delete_projects_by_slug parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes a project and takes its site off the internet. tags: - project x-app: project x-public: true x-tool: true get: description: "Returns one project of yours by slug \u2014 its settings, its live URL\nand the deployment currently serving\ \ it.\n\nScope: a validated principal is required (401 without one) and the lookup is\nkeyed by (org, slug), so another\ \ tenant's slug is a 404 exactly like a\nnonexistent one." operationId: get_projects_by_slug parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsProject' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one project of yours by slug \u2014 its settings, its live URL and the deployment currently serving\ \ it." tags: - project x-app: project x-public: true x-tool: true patch: description: "Changes a project's settings, and only the settings you send.\n\nEvery field is optional and absent means\ \ \"leave it\": `name` may not be blanked,\n`framework` must stay a known build hint, and `cacheControl` is capped\ \ at 256\ncharacters with no newlines (it becomes a response header). `visibility` flips\npublic/private under the\ \ same rule as create \u2014 public is free, private needs a\nfunded org. `upstream` and `license` are free-text credit\ \ for third-party work,\nand sending \"\" clears one. Changing anything reconciles the project's canonical\ngit repo,\ \ so a visibility change reaches the source and not just the listing.\n\n`hidden`/`hiddenReason` are platform MODERATION\ \ and are ignored unless the\ncaller is a platform admin; they remove a project from the public catalogue\nwithout\ \ touching the publisher's own visibility choice, so un-hiding restores\nexactly what they asked for.\n\nScope: a\ \ validated principal is required (401 without one) and the project is\nresolved within that principal's org, so another\ \ tenant's slug is a 404." operationId: patch_projects_by_slug parameters: - description: "Slug is the project to update, from the path. The URL is the addressing\nauthority \u2014 a `slug` in\ \ the body cannot move the write to another project." in: path name: slug required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsUpdate' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsProject' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Changes a project's settings, and only the settings you send. tags: - project x-app: project x-public: true x-tool: true /v1/projects/{slug}/deploy: post: description: "Takes a built site live at `https://.hanzo.app` in one call. The body is the site itself \u2014\ \ a `zip` or `tar.gz` holding `index.html` at its root (or a single wrapper directory that does), sent raw or as a\ \ multipart file part. It is unpacked to the site's own storage prefix and served immediately, answering the finished\ \ deployment.\n\nIt is bounded by the edge body limit (16 MiB by default), and that bound is the whole reason the\ \ other path exists: an oversized POST is refused by the server BEFORE any handler runs and surfaces as an opaque\ \ `400 Error when parsing request` that reads like a malformed payload rather than a size cap. A site too large for\ \ one archive opens a deployment with `POST /v1/project/{slug}/deployments` instead and writes its files straight\ \ to storage against the scoped grant that answers with \u2014 no body limit, and no bytes through this API at all.\n\ \nBilling is fail-closed and fails FIRST: the hosting gate runs before anything is parsed or uploaded, so an unfunded\ \ org is 402 and an unreachable commerce is 503 with nothing written. The debit lands only on success \u2014 a failed\ \ upload is never billed and never flips the live site \u2014 and a redeploy answers the SAME URL, because slug and\ \ apex are stable.\n\nScope: a validated principal is required (401 without one) and the site is resolved within that\ \ principal's org, so another tenant's slug is a 404. Object storage must be configured (503); an archive that does\ \ not walk is a 400 and one over the size cap is a 413." operationId: post_projects_by_slug_deploy parameters: - in: path name: slug required: true schema: type: string requestBody: content: application/octet-stream: schema: format: binary type: string responses: 2XX: content: application/json: schema: $ref: '#/components/schemas/projectsDeployment' description: Success summary: Upload a built site as one archive and serve it tags: - project x-app: project x-public: true /v1/projects/{slug}/deployments: get: description: "Returns a project's deploy history, newest version first.\n\nEvery deploy of the project is a row \u2014\ \ uploads, generated sites, and git/CI\nbuilds alike \u2014 carrying its version, status, source, commit, live URL,\ \ file\ncount and byte count. The short-lived upload grant a queued git deployment was\nhanded is NOT replayed here:\ \ it exists only on the 202 that minted it, so a\ngrant cannot outlive its build by being fetched again.\n\nScope:\ \ a validated principal is required (401 without one) and the project is\nresolved within that principal's org, so\ \ another tenant's slug is a 404." operationId: get_projects_by_slug_deployments parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/project.projectsDeployment' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns a project's deploy history, newest version first. tags: - project x-app: project x-public: true x-tool: true post: description: "Opens a deployment and hands back a short-lived, prefix-scoped\ngrant to write its bytes straight to object\ \ storage. Answers 202.\n\nThis is the path for a site too large to send as one archive: a real export is\nhundreds\ \ of megabytes against a 16 MiB body limit, so the bytes deliberately do\nNOT pass through the API. The answer carries\ \ `bucket`, `prefix` and `upload` \u2014\na presigned POST policy that S3 itself confines to this site's prefix\n\ (starts-with `//`), expires in 30 minutes and bounds each object.\nSo a build writes its own files and\ \ holds no standing bucket credential; there\nis nothing to rotate and nothing that leaks between tenants. Never guess\ \ the\nprefix \u2014 it is server-derived, and a guessed one lands where nothing is served.\n\nThe deployment is `queued`\ \ until POST .../deployments/{id}/complete flips it\nlive (or error). That completion is also where DELETION happens:\ \ the grant\nauthorizes writes only, so a build cannot remove a file, and cloud reconciles\nthe prefix against the\ \ `keys` manifest the completion carries. A build that\ndies before completing leaves the deployment queued rather\ \ than a half-live\nsite.\n\nThe grant is on the 202 and NOWHERE else \u2014 it is never stored and never\nreplayed\ \ on a later read, so it cannot outlive the build it was minted for. A\ndeployment whose grant could not be minted\ \ is still created and still\ncompletable; it simply carries no `upload`, and a caller with no other way to\nwrite\ \ should treat that as the failure it is.\n\nBilling: the hosting gate runs BEFORE anything is created (402 unfunded,\ \ 503\ncommerce unreachable), and the debit lands on the completion that goes live \u2014\nnever on a queued or failed\ \ build.\n\nScope: a validated principal is required (401 without one) and the site is\nresolved within that principal's\ \ org, so another tenant's slug is a 404." operationId: post_projects_by_slug_deployments parameters: - description: Slug is the site to deploy, from the path. in: path name: slug required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsDeployStart' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/project.projectsDeployment' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Opens a deployment and hands back a short-lived, prefix-scoped grant to write its bytes straight to object storage. tags: - project x-app: project x-public: true x-tool: true /v1/projects/{slug}/deployments/{id}: get: description: "Returns one deployment of a project by id.\n\nIt is how a console follows a build: the status (`queued`,\ \ `uploading`,\n`live`, `error`), the message a failure left, and the URL and prefix it went\nlive at. Like the history,\ \ it never replays the upload grant.\n\nScope: a validated principal is required (401 without one). Both the project\n\ and the deployment are resolved within that principal's org, so a deployment\nof another project \u2014 or of another\ \ tenant \u2014 is a 404." operationId: get_projects_by_slug_deployments_by_id parameters: - description: Slug is the project the deployment belongs to, from the path. in: path name: slug required: true schema: type: string - description: "ID is the deployment id, from the path. A deployment of another project \u2014\nor of another tenant's\ \ project \u2014 is not found." in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsDeployment' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one deployment of a project by id. tags: - project x-app: project x-public: true x-tool: true /v1/projects/{slug}/deployments/{id}/complete: post: description: "CompleteDeployment is the CI completion hook that flips a queued git\ndeployment to live (or error) once\ \ CI has synced the built site to S3.\n\n`status` must be `live` or `error`. On a LIVE completion the public host\ \ is\nclaimed FIRST, so the deployment reports the URL it actually OWNS \u2014 a\nCI-supplied `liveUrl` is a hint\ \ that can refine that URL but can never assert\na subdomain another tenant holds. `keys` is the manifest CI just\ \ uploaded,\nrelative to the deployment prefix: cloud reconciles the prefix against it so a\npage deleted from the\ \ build actually stops serving. Omit `keys` and nothing is\ndeleted \u2014 the prefix only grows. Reconciliation runs\ \ only on a live completion\n(pruning against a failed build's manifest would delete the site the last good\nbuild\ \ is still serving) and is best-effort, so a stale leftover never turns a\nsuccessful deploy into a 500. A live completion\ \ is also the one billable\nmoment on the git path; an error completion bills nothing.\n\nScope: a validated principal\ \ is required (401 without one). CI authenticates\nwith an org-scoped token through the gateway, so the deployment\ \ is resolved\nwithin that principal's org and another tenant's slug or deployment id is a\n404." operationId: post_projects_by_slug_deployments_by_id_complete parameters: - description: Slug is the project the deployment belongs to, from the path. in: path name: slug required: true schema: type: string - description: ID is the queued deployment to complete, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsComplete' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsDeployment' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: CompleteDeployment is the CI completion hook that flips a queued git deployment to live (or error) once CI has synced the built site to S3. tags: - project x-app: project x-public: true x-tool: true /v1/projects/{slug}/domains: get: description: "Returns every custom hostname this site holds: the live ones, plus\nany pending claim with the DNS records\ \ it still owes.\n\n`domains` is the routing answer \u2014 the hosts that are verified right now \u2014\nwhile `claims`\ \ is the full panel, one row per host, each saying whether it is\nlive or pending and, if pending, exactly what to\ \ publish.\n\nScope: a validated principal is required (401 without one) and the site is\nresolved within that principal's\ \ org, so another tenant's slug is a 404." operationId: get_projects_by_slug_domains parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsDomains' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns every custom hostname this site holds: the live ones, plus any pending claim with the DNS records it still owes.' tags: - project x-app: project x-public: true x-tool: true post: description: "Attaches one or more CUSTOM public hostnames to this org's site.\n\nBinding a host you do not own would\ \ let you shadow it at the edge, so which\noutcome you get depends on whether ownership is already established: a\ \ SuperAdmin\nvouches (the operator manages the customer's DNS, so its bind IS the proof) and\nbinds VERIFIED immediately;\ \ every other caller, INCLUDING an admin of the\ndeployment's own brand org, has the host CLAIMED as pending and gets\ \ the DNS\nchallenge back in `bound[].records`. A pending claim HOLDS the name so nobody\nelse can take it, but it\ \ does not route until POST .../domains/{host}/verify\nproves control.\n\nA hostname we operate is refused to a non-vouched\ \ caller (those are assigned\nby the platform, never claimed), a host another site already holds is a 409,\nand a\ \ name the platform holds is a 400 for EVERY caller \u2014 a vouch skips the\nownership proof, never the host table's\ \ own invariant. Claims and binds are\nidempotent for the same\n(org, slug), and re-claiming returns the SAME token\ \ rather than invalidating a\nrecord the customer has already published. The edge cache-tag is flushed\nafterwards\ \ so a newly-verified host serves the current build immediately.\n\nScope: a validated principal is required (401\ \ without one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404." operationId: post_projects_by_slug_domains parameters: - description: Slug is the site the hosts attach to, from the path. in: path name: slug required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsDomainsBind' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsBoundDomains' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Attaches one or more CUSTOM public hostnames to this org's site. tags: - project x-app: project x-public: true x-tool: true /v1/projects/{slug}/domains/{host}: delete: description: 'Gives a custom hostname back, so the name is free to reuse. A claim is FIRST-COME and global, so an add-only surface was not ownership but a leak: a customer who mistyped a domain, or claimed one they later moved elsewhere, could neither reuse it nor let anyone else. This is the third writer that closes it. The release is scoped to (host, org, slug), so it can only ever drop THIS tenant''s own claim, and it is IDEMPOTENT: releasing a host we do not hold is a clean 204, never a 404 that would let a caller probe which hosts other tenants hold. The edge cache-tag is flushed, since the host stops routing here. Scope: a validated principal is required (401 without one) and the site is resolved within that principal''s org, so another tenant''s slug is a 404.' operationId: delete_projects_by_slug_domains_by_host parameters: - description: Slug is the project the host is attached to, from the path. in: path name: slug required: true schema: type: string - description: 'Host is the custom hostname, from the path. It is cleaned to its canonical form (lowercased, trailing dot dropped) before anything is looked up.' in: path name: host required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Gives a custom hostname back, so the name is free to reuse. tags: - project x-app: project x-public: true x-tool: true /v1/projects/{slug}/domains/{host}/verify: post: description: "Checks the DNS challenge for a pending custom hostname and, when\nit passes, promotes the host so it begins\ \ routing at the edge.\n\nIt answers 200 either way, with the host's honest current state: verified once\nthe TXT\ \ record is found, still pending \u2014 with the records to publish and the\nresolver's own explanation in `detail`\ \ \u2014 when it is not. A not-yet is not an\nerror: the check ran, DNS simply has not propagated, and the customer\ \ retries.\nAn already-verified host is returned unchanged without re-resolving. On a\nsuccessful promotion the edge\ \ cache-tag is flushed, since the host routes as\nof that moment.\n\nScope: a validated principal is required (401\ \ without one). Both the site and\nthe claim are resolved within that principal's org, so a host claimed by\nanother\ \ tenant is \"not claimed by this site\"." operationId: post_projects_by_slug_domains_by_host_verify parameters: - description: Slug is the project the host is attached to, from the path. in: path name: slug required: true schema: type: string - description: 'Host is the custom hostname, from the path. It is cleaned to its canonical form (lowercased, trailing dot dropped) before anything is looked up.' in: path name: host required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsDomain' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Checks the DNS challenge for a pending custom hostname and, when it passes, promotes the host so it begins routing at the edge. tags: - project x-app: project x-public: true x-tool: true /v1/projects/{slug}/publish: post: description: "Promotes a build output into a new release AND goes live with it \u2014\ncreate+activate in one call,\ \ which is the 99% path.\n\nIt is exactly the two halves in sequence with no extra semantics, so the\nstaged flow\ \ and the one-shot flow can never drift apart: `source` is promoted\nunder the same org-relative rule and the same\ \ guards CreateRelease applies,\nthen the site's pointer is flipped to it, the public host is claimed and the\nedge\ \ is purged. Idempotent on unchanged bytes \u2014 same manifest, same release id,\nno copy \u2014 and billed once,\ \ after the release exists.\n\nScope: a validated principal is required (401 without one) and the site is\nresolved\ \ within that principal's org, so another tenant's slug is a 404." operationId: post_projects_by_slug_publish parameters: - description: Slug is the site to publish, from the path. in: path name: slug required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsPublish' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsRelease' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Promotes a build output into a new release AND goes live with it \u2014 create+activate in one call, which\ \ is the 99% path." tags: - project x-app: project x-public: true x-tool: true /v1/projects/{slug}/purge: post: description: "Flushes the site's edge cache without redeploying anything.\n\nIt invalidates the edge cache-tag `site--`\ \ and stamps `lastPurgeAt`\n(unix seconds), and it NEVER writes or deletes the S3 origin \u2014 the live build\nkeeps\ \ serving; only stale copies held at the edge drop, so the next request\nre-fetches the current artifact from origin.\ \ Idempotent, and an edge that is\nunconfigured or failing is not fatal: `lastPurgeAt` is still stamped and the\n\ answer is still the updated project.\n\nScope: a validated principal is required (401 without one) and the project\ \ is\nresolved within that principal's org, so another tenant's slug is a 404." operationId: post_projects_by_slug_purge parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsProject' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Flushes the site's edge cache without redeploying anything. tags: - project x-app: project x-public: true x-tool: true /v1/projects/{slug}/releases: get: description: "Returns a site's releases newest-first, marking the active one \u2014\nthe rollback menu.\n\nEach row\ \ carries the release id to activate, the source it was promoted from,\nits object and byte counts, and the URL if\ \ it is the one serving. Retention\nbounds the list, so it is the set that can actually still be rolled back to,\n\ not a full history.\n\nScope: a validated principal is required (401 without one) and the site is\nresolved within\ \ that principal's org, so another tenant's slug is a 404." operationId: get_projects_by_slug_releases parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/project.projectsRelease' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns a site's releases newest-first, marking the active one \u2014 the rollback menu." tags: - project x-app: project x-public: true x-tool: true post: description: "Promotes a build output into a new immutable release WITHOUT\nserving it \u2014 the staged half of publishing,\ \ for when you want to check a\nrelease before it goes live. Answers 201.\n\n`source` is a path RELATIVE to your org's\ \ own storage space: the org segment\nis prepended server-side from the validated principal and the bucket is never\n\ in the request at all, so a server-side copy can only ever reach bytes your\norg already owns. The prefix is listed,\ \ content-addressed (SHA-256 over the\nsorted manifest of key/size/etag), and copied into an immutable\n`/.releases///`\ \ prefix; the row is written LAST, so a partial\ncopy is unreachable rather than merely unlikely. Re-publishing an\ \ unchanged\nsource is idempotent BY CONSTRUCTION \u2014 same bytes, same id, no copy at all.\n\nThe source must contain\ \ index.html at its root and stay under the same file\nand byte caps an artifact deploy does (413 past them); a source\ \ that changes\nmid-copy is a 409 and the release is abandoned. Each publish also reclaims\nreleases past the retention\ \ depth, so a site's release space stays bounded.\nThis is the billable half \u2014 the hosting gate runs before any\ \ copy, and the\ndebit lands once the release exists.\n\nScope: a validated principal is required (401 without one)\ \ and the site is\nresolved within that principal's org, so another tenant's slug is a 404." operationId: post_projects_by_slug_releases parameters: - description: Slug is the site to publish, from the path. in: path name: slug required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/project.projectsPublish' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/project.projectsRelease' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Promotes a build output into a new immutable release WITHOUT serving it \u2014 the staged half of publishing,\ \ for when you want to check a release before it goes live." tags: - project x-app: project x-public: true x-tool: true /v1/projects/{slug}/releases/{release}/activate: post: description: "Points the site at an existing release \u2014 the go-live, and\nequally the ROLLBACK.\n\nAim it at an\ \ older release and the site serves that one again: releases are\nimmutable and retained to the retention depth, so\ \ nothing is rebuilt or\nre-copied and the flip is one atomic statement. Before the flip, two\nconditions run in the\ \ order that gives each its own honest answer \u2014 the ROW\nsays whether this release exists for this tenant at\ \ all (404, with no signal\nabout a foreign id), and only then do the BYTES say whether it can still serve\n(410 GONE\ \ when retention has reclaimed them; that rollback target is not\ncoming back, so publish again). Going live also\ \ claims the public host and\npurges the edge, so the release is reachable and no cached predecessor is\nserved. NOT\ \ billed: no new content is produced, only a pointer moved.\n\nScope: a validated principal is required (401 without\ \ one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404." operationId: post_projects_by_slug_releases_by_release_activate parameters: - description: Slug is the site the release belongs to, from the path. in: path name: slug required: true schema: type: string - description: 'Release is the content-addressed release id ("rel_" + 32 hex chars), from the path. Anything that is not that shape is not found, rather than being interpolated into a storage prefix.' in: path name: release required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsRelease' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Points the site at an existing release \u2014 the go-live, and equally the ROLLBACK." tags: - project x-app: project x-public: true x-tool: true /v1/projects/{slug}/shot: get: description: "Returns a screenshot of what this project currently serves, as image/png. The capture is keyed by the\ \ deployment, so a redeploy invalidates it by construction rather than by anyone remembering to clear a cache. A project\ \ with nothing deployed answers 404 \u2014 that is a 404 about the PICTURE and not about the project, which is still\ \ right there in the list. Scoped to the caller's org: a validated principal is required, and a slug belonging to\ \ another org is not found rather than forbidden." operationId: get_projects_by_slug_shot parameters: - in: path name: slug required: true schema: type: string summary: Get a PNG of the project's live site tags: - project x-app: project x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/projects/{slug}/star: delete: description: "Removes the caller's own bookmark from a project, and answers whether\nit is starred afterwards.\n\nIt\ \ removes only YOUR star \u2014 the same one star wrote \u2014 so a project other\npeople have starred stays on their\ \ lists. Unstarring one you had not starred\nis not an error; it leaves it unstarred." operationId: delete_projects_by_slug_star parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsStar' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes the caller's own bookmark from a project, and answers whether it is starred afterwards. tags: - project x-app: project x-public: true x-tool: true put: description: 'Bookmarks a project for the person calling, and answers whether it is starred afterwards. The star is YOURS: it is keyed by you as well as by the project, so two people see two answers for the same one and starring it says nothing about anybody else''s list. Starring a project you have already starred leaves it starred.' operationId: put_projects_by_slug_star parameters: - description: 'Slug is the project to act on, from the path. It is unique within the caller''s org and nowhere else, so another tenant''s slug is a 404.' in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/project.projectsStar' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Bookmarks a project for the person calling, and answers whether it is starred afterwards. tags: - project x-app: project x-public: true x-tool: true /v1/prompt: get: description: "Returns the caller org's prompt library as one row per prompt: its name,\ntype, every version number it\ \ has, its taxonomy and when it last changed. The\ntemplate bodies are deliberately absent \u2014 fetch one prompt\ \ to read its text." operationId: get_prompt responses: '200': content: application/json: schema: $ref: '#/components/schemas/prompt.promptList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the caller org''s prompt library as one row per prompt: its name, type, every version number it has, its taxonomy and when it last changed.' tags: - prompt x-app: prompt x-public: true x-tool: true post: description: 'Records a prompt for the caller''s org and answers 201 with it. A name the org already uses is NOT an error and NOT an overwrite: it appends a new version, so the library keeps real, inspectable history and the response carries the whole version list. The name is also the URL segment the prompt is fetched by, which is why its shape is constrained and a handful of names are reserved.' operationId: post_prompt requestBody: content: application/json: example: name: greeting prompt: You are a helpful assistant. tags: - support schema: $ref: '#/components/schemas/prompt.promptReq' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/prompt.promptDetail' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records a prompt for the caller's org and answers 201 with it. tags: - prompt x-app: prompt x-public: true x-tool: true /v1/prompt/catalog: get: description: "Returns the read-only starter prompt library shipped with the binary \u2014\nreference content every tenant\ \ sees the same, NOT the caller's own prompts and\nnever mixed into them. An org's library stays honestly empty until\ \ someone\nexplicitly imports a starter, which is an ordinary POST /v1/prompt. Entries that\nwould fail the create\ \ guards are dropped, so everything offered here can actually\nbe imported." operationId: get_prompt_catalog responses: '200': content: application/json: schema: $ref: '#/components/schemas/prompt.catalogList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the read-only starter prompt library shipped with the binary \u2014 reference content every tenant\ \ sees the same, NOT the caller's own prompts and never mixed into them." tags: - prompt x-app: prompt x-public: true x-tool: true /v1/prompt/metrics: get: description: "Returns real per-prompt statistics for the caller's org: how many versions\neach prompt has, which one\ \ is current, and when it was created and last changed.\nEvery number is counted from the store \u2014 nothing here\ \ is estimated or fabricated." operationId: get_prompt_metrics responses: '200': content: application/json: schema: $ref: '#/components/schemas/prompt.metricList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns real per-prompt statistics for the caller''s org: how many versions each prompt has, which one is current, and when it was created and last changed.' tags: - prompt x-app: prompt x-public: true x-tool: true /v1/prompt/{name}: delete: description: 'Delete removes one of the caller org''s prompts and every version of it, answering 204. It is scoped to the caller''s org, so a name another tenant owns is the same 404 an unknown name gives. There is no undo: the version history goes with it.' operationId: delete_prompt_by_name parameters: - description: Name is the prompt to act on, from the path. example: greeting in: path name: name required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Delete removes one of the caller org's prompts and every version of it, answering 204. tags: - prompt x-app: prompt x-public: true x-tool: true get: description: "Returns one of the caller org's prompts: its CURRENT template text plus the\nmetadata of every version\ \ it has had. The history carries version numbers, types\nand timestamps only \u2014 not each version's body \u2014\ \ so a long history cannot inflate\nthis response. A name the caller's org does not own is 404, whoever owns it." operationId: get_prompt_by_name parameters: - description: Name is the prompt to act on, from the path. example: greeting in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/prompt.promptDetail' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one of the caller org''s prompts: its CURRENT template text plus the metadata of every version it has had.' tags: - prompt x-app: prompt x-public: true x-tool: true /v1/provider: get: description: "Returns every registered integration provider together with THIS org's\nconnection status for it \u2014\ \ the catalog the console's Integrations page renders.\nOrg-authed: a caller with no validated principal is 403, because\ \ the status is\nper-org and there is no org-less answer. User-plane providers (the /v1/connection\nsurface) are omitted;\ \ the two planes are disjoint." operationId: get_provider_2 responses: '200': content: application/json: example: providers: - available: true category: Communication connected: true connection: account: Acme connectedAt: '2026-07-01T10:00:00Z' externalId: T0231 scopes: - chat:write description: Connect your workspace. id: slack name: Slack schema: $ref: '#/components/schemas/provider.listOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns every registered integration provider together with THIS org's connection status for it \u2014 the\ \ catalog the console's Integrations page renders." tags: - provider x-app: provider x-public: true x-tool: true x-id: get_provider /v1/provider/discord/interactions: post: description: "The Interactions Endpoint URL for the Discord app. It answers Discord's PING with a PONG, and handles\ \ the `/hanzo` slash command by acknowledging with a deferred ephemeral reply and editing that reply with the answer\ \ once the agent has run. Any other interaction is acknowledged and ignored.\n\nRequests are verified by ED25519 SIGNATURE\ \ over the timestamp and body against the app's public key \u2014 not by HMAC, unlike the Slack webhooks. Interactions\ \ work over plain HTTP, so no gateway connection and no message-content intent is involved.\n\nDiscord does not retry,\ \ so this is the one channel where being at capacity is shown to the user as an ephemeral ask-to-run-it-again rather\ \ than answered as a retriable failure \u2014 nothing is recorded either way, so the next attempt is clean.\n\nThe\ \ caller here is the PLATFORM, not a Hanzo tenant, so there is no bearer and no principal. The signature check IS\ \ the authentication, and it fails closed. The tenant is never read from the payload either: it is resolved from the\ \ verified platform identifier through the connection map, so an event from a workspace nobody connected does nothing.\ \ Refusals are written with their own status rather than being flattened to a 500, so a rejected signature reads as\ \ 401 and a malformed body as 400." operationId: post_provider_discord_interactions summary: Discord interactions endpoint tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/discord/link: get: description: "The entry point behind the connect prompt Hanzo shows in a Discord server. It starts a link session and\ \ redirects to Discord's OAuth `identify` consent \u2014 the narrowest scope that establishes which Discord user is\ \ asking, and nothing more.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser\ \ is expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted\ \ and carries the CHAT it started from \u2014 it is provenance only, and it never decides which account gets linked.\ \ The account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding\ \ a link to someone else cannot bind their account, and a session lifted into another browser is refused rather than\ \ completed. Each link is single-use, and a deployment without linking configured answers 503." operationId: get_provider_discord_link summary: Begin linking a Hanzo account from Discord tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/discord/link/callback: get: description: "The final leg: it binds the verified Discord user to the Hanzo account that just signed in, and answers\ \ a short confirmation page telling them to return to Discord. The Hanzo credential is sealed into the connected org's\ \ KMS namespace rather than stored beside the link.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable:\ \ a browser is expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted\ \ and carries the CHAT it started from \u2014 it is provenance only, and it never decides which account gets linked.\ \ The account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding\ \ a link to someone else cannot bind their account, and a session lifted into another browser is refused rather than\ \ completed. Each link is single-use, and a deployment without linking configured answers 503." operationId: get_provider_discord_link_callback summary: Complete the Discord account link tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/discord/link/discord: get: description: "Where Discord returns the user after the identify consent. It resolves the verified Discord user, confirms\ \ the server is connected to an org, and hands the browser to the Hanzo sign-in that completes the link.\n\nThis is\ \ one leg of a three-leg flow, and the legs are not interchangeable: a browser is expected to arrive here only from\ \ the leg before it. The link URL's state proves the prompt was server-minted and carries the CHAT it started from\ \ \u2014 it is provenance only, and it never decides which account gets linked. The account identity always comes\ \ from the platform's own verified sign-in and a host-bound cookie, so forwarding a link to someone else cannot bind\ \ their account, and a session lifted into another browser is refused rather than completed. Each link is single-use,\ \ and a deployment without linking configured answers 503." operationId: get_provider_discord_link_discord summary: Discord sign-in return leg tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/forge/webhook: post: description: Receives the forge's workflow_job delivery. The signature over the raw body is checked against the secret at KMS forge.WebhookRef before anything is decoded; a queued job becomes one ephemeral runner Job on the cluster, minted a registration token for exactly that job. Every other action is answered 200 and ignored. operationId: post_provider_forge_webhook requestBody: content: application/json: schema: $ref: '#/components/schemas/forgeJob' responses: 2XX: content: application/json: schema: $ref: '#/components/schemas/forgeLaunched' description: Success summary: Forge workflow_job webhook tags: - provider x-app: provider x-public: true /v1/provider/github/claim: post: description: "Binds installations the App ALREADY holds to the org the caller is\nacting in \u2014 the reconciliation\ \ for a grant that happened outside our connect\nflow.\n\nAn installation IS the grant: GitHub recorded the consent\ \ when the App was\ninstalled, and our connection row is bookkeeping that never got written because\nnobody came through\ \ our callback. This writes that row from the App's own view,\nso 23 accounts granted straight from GitHub stop reading\ \ as nothing.\n\nThe org is taken from the VALIDATED PRINCIPAL and never from the body, because\nit is the one part\ \ GitHub cannot tell us. An installation carries an account\nlogin, a type and a repository selection \u2014 nothing\ \ that names a Hanzo org. So\nthe binding cannot be DERIVED, only asserted, and the only unforgeable assertion\navailable\ \ is the org the caller is already acting in. Inferring one from the\naccount name would be a guess the store cannot\ \ catch: its key is\n(org,provider,owner), so a wrong org is a valid row, and a valid row is a\nmirror pointed at\ \ the wrong tenant.\n\nSUPER ADMIN only, for that same reason. A tenant's proof that an account is\ntheirs is GitHub's\ \ own consent screen \u2014 the connect flow \u2014 and without it any\norg could claim any account the App holds.\ \ Platform sudo is already the scope\nthat reads the whole install list, so it is the scope that may bind from it;\n\ giving a tenant this verb would hand it every other tenant's repositories.\n\nIdempotent: the row is keyed (org,provider,owner)\ \ and connected_at survives an\nupsert, so claiming twice rebinds the same account to the same org and reports\nit\ \ under `already`. Re-claiming also REFRESHES the installation id, so an\naccount reinstalled on GitHub \u2014 new\ \ id, same login \u2014 self-heals instead of\nminting tokens against a dead installation.\n\nClaiming an account\ \ another org holds ADDS this org's row and leaves theirs\nstanding, so no org loses an integration it is using." operationId: post_provider_github_claim requestBody: content: application/json: schema: $ref: '#/components/schemas/provider.githubClaimIn' required: true responses: '200': content: application/json: example: already: - zooai claimed: - hanzoai - luxfi schema: $ref: '#/components/schemas/provider.githubClaimOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Binds installations the App ALREADY holds to the org the caller is acting in \u2014 the reconciliation for\ \ a grant that happened outside our connect flow." tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/github/fork: post: description: "Forks a granted repository.\n\nGitHub's fork is ASYNCHRONOUS: it answers 202 with the target repo and\n\ populates it in the background, and it answers the same 202 when the fork\nalready exists. So this reports what GitHub\ \ said rather than waiting \u2014 a call\nthat blocked until the clone finished would time out on a large repository\ \ and\ntell the caller nothing it does not already know." operationId: post_provider_github_fork requestBody: content: application/json: example: repo: widgets schema: $ref: '#/components/schemas/provider.githubForkReq' required: true responses: '200': content: application/json: example: clone_url: https://github.com/acme/widgets.git default_branch: main existing: false full_name: acme/widgets html_url: https://github.com/acme/widgets schema: $ref: '#/components/schemas/provider.githubForkOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Forks a granted repository. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/github/installations: get: description: "Lists the GitHub accounts the caller may see the App\ninstalled on, each confirmed against the App's own\ \ list, plus where to add\nanother.\n\nThe confirmation is the point. A connection row holds an installation id, and\n\ an id whose installation was since removed on GitHub is a row that mints\nnothing \u2014 every list and import against\ \ it fails with a token error, which\nreads as \"our git integration is broken\" rather than \"that install is gone\"\ .\nChecking the App's view turns that into a fact the caller can act on.\n\nORG-SCOPED for a tenant, deliberately.\ \ The App is installed across every\ncustomer, so the raw list is the customer list; a tenant sees only accounts its\n\ own org has bound. It discovers a NEW account by installing it (InstallURL),\nwhich is GitHub's own consent screen\ \ \u2014 not by reading ours.\n\nA SUPER ADMIN sees the App's whole install list, because that list is the\nplatform's\ \ own inventory rather than any one tenant's data, and platform sudo\nis the single cross-tenant scope this house\ \ has. Without it an App installed\nout-of-band \u2014 granted straight from GitHub, so no connect flow ever ran and\ \ no\nconnection row exists \u2014 is invisible to everyone: the console card reads \"not\nconnected\" and an operator\ \ asked \"which GitHub orgs do you see\" can only\nanswer for accounts already bound, which is precisely the accounts\ \ that were\nnever the question." operationId: get_provider_github_installations responses: '200': content: application/json: example: installUrl: https://github.com/apps/hanzo/installations/new installations: - connected: true grant: all htmlUrl: https://github.com/hanzoai login: hanzoai type: Organization schema: $ref: '#/components/schemas/provider.githubInstallationsOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the GitHub accounts the caller may see the App installed on, each confirmed against the App's own list, plus where to add another. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/github/issues/backfill: post: description: "Seeds the native todo with the EXISTING issues across the\norg's granted repos (default state=open); the\ \ webhook keeps them live thereafter.\nOrg-scoped by the validated principal \u2014 a caller only ever backfills its\ \ OWN org.\nSynchronous + bounded (a total time budget and an issue cap) so it returns the\ncounts directly; idempotent\ \ by ExtRef, so a re-run continues where a truncated\npass left off and never duplicates." operationId: post_provider_github_issues_backfill requestBody: content: application/json: example: state: all schema: $ref: '#/components/schemas/provider.githubBackfillIn' required: true responses: '200': content: application/json: example: created: 410 failed: 0 issues: 430 repos: 12 updated: 20 schema: $ref: '#/components/schemas/provider.githubBackfillResult' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Seeds the native todo with the EXISTING issues across the org's granted repos (default state=open); the webhook keeps them live thereafter. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/github/repos: get: description: 'Lists the GitHub repositories the caller may work on, most recently pushed first, searched and paged on the server. A person who connected their own GitHub sees what their token reaches in this App''s installations. An org admin who has not sees every repository in the installations their org holds. Anyone else gets an empty page with connected:false, which is the console''s cue to ask them to connect.' operationId: get_provider_github_repos parameters: - description: Q keeps repositories whose owner/name contains it, case-insensitively. example: cloud in: query name: q required: false schema: type: string - description: "Owner keeps one GitHub account's repositories \u2014 an org or a user login." in: query name: owner required: false schema: type: string - description: 'Limit is the page size: 1 to 100, and 50 when absent or unreadable.' example: 50 in: query name: limit required: false schema: format: int64 type: integer - description: 'After is the `next` of the previous page. Absent starts at the most recently pushed.' in: query name: after required: false schema: type: string responses: '200': content: application/json: example: connected: true next: '' repos: - default_branch: main full_name: hanzoai/cloud installation_id: 62000701 name: cloud owner: hanzoai private: false pushed_at: '2026-09-24T18:00:00Z' total: 1 schema: $ref: '#/components/schemas/provider.githubReposOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the GitHub repositories the caller may work on, most recently pushed first, searched and paged on the server. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/github/repos/import: post: description: 'Imports the selected (or all) granted repos into git.hanzo.ai. The selection is intersected with the installation''s GRANTED set, so a client can never import a repo the App was not granted (org isolation + a grant check). The import runs in a bounded background worker (don''t block the request), so the answer is 202 Accepted; poll GET /v1/git/repos for each repository to appear on git.hanzo.ai.' operationId: post_provider_github_repos_import requestBody: content: application/json: example: repos: - widgets schema: $ref: '#/components/schemas/provider.githubImportIn' required: true responses: '202': content: application/json: example: queued: 1 repos: - widgets schema: $ref: '#/components/schemas/provider.githubImportOut' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Imports the selected (or all) granted repos into git.hanzo.ai. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/github/repos/{owner}/{repo}/branches: get: description: "Lists a repository's branches, the default first, with prefix\nsearch. The repository must be one the\ \ caller may see \u2014 the same set\nGET /v1/provider/github/repos lists \u2014 or it is 404." operationId: get_provider_github_repos_by_owner_by_repo_branches parameters: - description: Owner is the GitHub account that holds the repository. example: hanzoai in: path name: owner required: true schema: type: string - description: Repo is the repository's name within that account. example: cloud in: path name: repo required: true schema: type: string - description: Q keeps branches whose name starts with it, matched by GitHub. example: feat/ in: query name: q required: false schema: type: string - description: 'Limit is the page size: 1 to 100, and 50 when absent or unreadable.' in: query name: limit required: false schema: format: int64 type: integer - description: After is the `next` of the previous page. in: query name: after required: false schema: type: string responses: '200': content: application/json: example: branches: - commit: 9f2c1e7a default: true name: main - commit: 4b8d0e3f default: false name: feat/login next: '' total: 2 schema: $ref: '#/components/schemas/provider.githubBranchesOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists a repository's branches, the default first, with prefix search. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/github/repos/{repo}/pages: delete: description: 'Deletes the repo''s Pages site. 404 when there is none, so a caller can tell "turned it off" from "there was nothing on".' operationId: delete_provider_github_repos_by_repo_pages parameters: - description: 'Repo is the repository''s short name within the org''s installation, with no owner prefix (the owner is server-derived from the grant). A trailing ".git" is stripped.' example: widgets in: path name: repo required: true schema: type: string responses: '200': content: application/json: example: disabled: true repo: widgets schema: $ref: '#/components/schemas/provider.githubPagesDisabledOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes the repo's Pages site. tags: - provider x-app: provider x-public: true x-tool: true get: description: 'Returns the repo''s Pages status, live URL, custom domain and build source. The repo is resolved against the org installation''s GRANTED set, so a caller can never address a repo the App was not granted; 404 when the repo has no Pages site.' operationId: get_provider_github_repos_by_repo_pages parameters: - description: 'Repo is the repository''s short name within the org''s installation, with no owner prefix (the owner is server-derived from the grant). A trailing ".git" is stripped.' example: widgets in: path name: repo required: true schema: type: string responses: '200': content: application/json: example: buildType: legacy cname: docs.acme.com custom404: false httpsEnforced: true repo: widgets source: branch: main path: /docs status: built url: https://acme.github.io/widgets/ schema: $ref: '#/components/schemas/provider.githubPagesView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the repo's Pages status, live URL, custom domain and build source. tags: - provider x-app: provider x-public: true x-tool: true post: description: 'Creates the repo''s Pages site and answers 201 Created with it. With buildType "workflow" the site builds via GitHub Actions; otherwise it builds from a branch source, defaulting to the repo''s own default branch when none is given. Only "/" and "/docs" are legal source paths (GitHub''s rule).' operationId: post_provider_github_repos_by_repo_pages parameters: - description: Repo is the repository, from the :repo path segment. example: widgets in: path name: repo required: true schema: type: string requestBody: content: application/json: example: branch: main path: /docs repo: widgets schema: $ref: '#/components/schemas/provider.githubPagesEnableReq' required: true responses: '200': content: application/json: example: buildType: legacy custom404: false httpsEnforced: true repo: widgets source: branch: main path: /docs status: building url: https://acme.github.io/widgets/ schema: $ref: '#/components/schemas/provider.githubPagesView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates the repo's Pages site and answers 201 Created with it. tags: - provider x-app: provider x-public: true x-tool: true put: description: 'Sets or clears the custom domain (cname) and updates HTTPS enforcement, build type, or source. ONLY the provided fields are sent to GitHub, so an update never resets a setting the caller did not mention.' operationId: put_provider_github_repos_by_repo_pages parameters: - description: Repo is the repository, from the :repo path segment. example: widgets in: path name: repo required: true schema: type: string requestBody: content: application/json: example: cname: docs.acme.com httpsEnforced: true repo: widgets schema: $ref: '#/components/schemas/provider.githubPagesUpdateReq' required: true responses: '200': content: application/json: example: repo: widgets updated: true schema: $ref: '#/components/schemas/provider.githubPagesUpdatedOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Sets or clears the custom domain (cname) and updates HTTPS enforcement, build type, or source. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/github/repos/{repo}/pages/builds: post: description: 'Requests a Pages rebuild and returns the queued build''s status. The build is queued AT GITHUB, not completed here, so the answer is 202 Accepted and its status is the one GitHub reported at queue time. 404 when the repository has no Pages site, or when the org''s installation was not granted it.' operationId: post_provider_github_repos_by_repo_pages_builds parameters: - description: 'Repo is the repository''s short name within the org''s installation, with no owner prefix (the owner is server-derived from the grant). A trailing ".git" is stripped.' example: widgets in: path name: repo required: true schema: type: string responses: '202': content: application/json: example: repo: widgets status: queued url: https://api.github.com/repos/acme/widgets/pages/builds/1 schema: $ref: '#/components/schemas/provider.githubPagesBuildOut' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Requests a Pages rebuild and returns the queued build's status. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/github/search: post: description: "Finds repositories on GitHub.\n\nThis reads the PUBLIC index and returns nothing an installation unlocks:\ \ it is\nhow you find a repository to fork, not a way to see inside one. The org's own\ntoken is used only so the\ \ query is rate-limited against the installation\nrather than anonymously \u2014 the results are the same ones anyone\ \ would get." operationId: post_provider_github_search requestBody: content: application/json: example: limit: 5 q: language:go raft schema: $ref: '#/components/schemas/provider.githubSearchReq' required: true responses: '200': content: application/json: example: count: 1 repos: - full_name: hashicorp/raft language: Go stars: 8000 schema: $ref: '#/components/schemas/provider.githubSearchOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Finds repositories on GitHub. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/github/user: get: description: 'Reports whether the caller has connected their own GitHub account in the org they act in, and as whom.' operationId: get_provider_github_user responses: '200': content: application/json: example: configured: true connected: true connectedAt: '2026-09-24T12:00:00Z' expiresAt: '2026-09-24T20:00:00Z' login: octocat schema: $ref: '#/components/schemas/provider.githubUserOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports whether the caller has connected their own GitHub account in the org they act in, and as whom. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/github/user/callback: get: description: 'Is where GitHub returns the person. It verifies the state, burns its nonce, and parks the code for the console to complete; it exchanges nothing. PUBLIC and state-authed, like every OAuth return, and raw because it answers a browser with a redirect.' operationId: get_provider_github_user_callback summary: Is where GitHub returns the person. tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/github/user/complete: post: description: "Finishes connecting the caller's GitHub account: it takes\nthe authorization the callback parked, trades\ \ it for the person's token, and\nseals it. Only the person who started the flow can complete it \u2014 another\n\ person's grant id is simply not found \u2014 and a grant completes once." operationId: post_provider_github_user_complete requestBody: content: application/json: example: grant: 9f2c1e7a4b8d0e3f6a5b2c1d0e9f8a7b schema: $ref: '#/components/schemas/provider.githubUserCompleteIn' required: true responses: '200': content: application/json: example: configured: true connected: true connectedAt: '2026-09-24T12:00:00Z' expiresAt: '2026-09-24T20:00:00Z' login: octocat schema: $ref: '#/components/schemas/provider.githubUserOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Finishes connecting the caller''s GitHub account: it takes the authorization the callback parked, trades it for the person''s token, and seals it.' tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/github/user/connect: post: description: 'Begins connecting the caller''s own GitHub account: it answers the App''s authorization page, carrying a state signed for this org and this person. Every member may connect their own; it grants nothing the person does not already hold on GitHub.' operationId: post_provider_github_user_connect responses: '200': content: application/json: example: authorizeUrl: https://github.com/login/oauth/authorize?client_id=Iv23li3SYLoq40ExR6EN&redirect_uri=...&state=... schema: $ref: '#/components/schemas/provider.githubUserConnectOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Begins connecting the caller''s own GitHub account: it answers the App''s authorization page, carrying a state signed for this org and this person.' tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/github/user/disconnect: post: description: 'Forgets the caller''s GitHub connection in this org: the token is revoked at GitHub, and the sealed secrets and the row go. It says whether GitHub confirmed the revocation rather than reporting a success it did not see. Disconnecting what is not connected succeeds.' operationId: post_provider_github_user_disconnect responses: '200': content: application/json: example: disconnected: true revoked: true schema: $ref: '#/components/schemas/provider.githubUserDisconnectOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Forgets the caller''s GitHub connection in this org: the token is revoked at GitHub, and the sealed secrets and the row go.' tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/github/webhook: post: description: "The address the GitHub App delivers events to. A push is handed to the repository sync engine, and an\ \ issue or issue-comment event is mirrored into the native todo \u2014 idempotently, so the same issue re-syncs to\ \ one row however many times it is edited, closed or reopened. A repository event that puts a repo INTO the granted\ \ set (created, transferred, unarchived) raises one todo offering to import it; accepting means POSTing that repo\ \ to /v1/provider/github/import. It never imports on its own.\n\nIt answers a benign 200 for everything it does not\ \ act on \u2014 the ping, other event types, an unknown installation \u2014 deliberately, so GitHub does not enter\ \ a retry storm over events that were never going to do anything. Only a bad signature and a genuine sync failure\ \ are non-200, and an oversized payload is refused outright.\n\nTwo sync rules are worth stating because neither is\ \ guessable. EVERY ref syncs, tags as well as branches, because releases are cut by tag and filtering them would stop\ \ publishing with nothing reporting a failure. And a delete is NEVER propagated: the native side is canonical, so\ \ an inbound delete never removes a native ref.\n\nThe payload is verified by HMAC against the webhook secret before\ \ it is parsed.\n\nThe caller here is the PLATFORM, not a Hanzo tenant, so there is no bearer and no principal. The\ \ signature check IS the authentication, and it fails closed. The tenant is never read from the payload either: it\ \ is resolved from the verified platform identifier through the connection map, so an event from a workspace nobody\ \ connected does nothing. Refusals are written with their own status rather than being flattened to a 500, so a rejected\ \ signature reads as 401 and a malformed body as 400." operationId: post_provider_github_webhook summary: GitHub App webhook tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/gitlab/projects: get: description: "Lists the projects the org's GitLab connection can reach \u2014\nmembership projects, most recently active\ \ first." operationId: get_provider_gitlab_projects responses: '200': content: application/json: example: account: acme projects: - cloneUrl: https://gitlab.com/acme/widgets.git defaultBranch: main fullName: acme/widgets htmlUrl: https://gitlab.com/acme/widgets name: widgets private: true pushedAt: '2026-07-01T10:00:00Z' schema: $ref: '#/components/schemas/provider.gitlabProjectsOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the projects the org's GitLab connection can reach \u2014 membership projects, most recently active\ \ first." tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/linear/claim: post: description: 'Binds the caller''s Linear organization to the org and seals the webhook secret. The organization is READ from the caller''s own key, never taken from the body: a person can only bind an organization they are a member of. An organization another org already holds is refused.' operationId: post_provider_linear_claim requestBody: content: application/json: example: secret: "whsec_\u2026" schema: $ref: '#/components/schemas/provider.linearClaimIn' required: true responses: '200': content: application/json: example: name: acme organization: "6f0b\u2026" path: /v1/provider/linear/webhook schema: $ref: '#/components/schemas/provider.linearClaimOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Binds the caller's Linear organization to the org and seals the webhook secret. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/linear/comments: post: description: 'Posts a comment on a Linear issue with the caller''s own key, so it carries their name. This is the op an agent is offered when it should answer in Linear rather than in chat.' operationId: post_provider_linear_comments requestBody: content: application/json: example: body: 'Reproduced on main; fix in #482.' issue: ENG-123 schema: $ref: '#/components/schemas/provider.linearCommentIn' required: true responses: '201': content: application/json: example: id: "c0f1\u2026" url: https://linear.app/acme/issue/ENG-123#comment-c0f1 schema: $ref: '#/components/schemas/provider.linearCommentOut' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Posts a comment on a Linear issue with the caller's own key, so it carries their name. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/linear/issues/backfill: post: description: 'Seeds the native todo with the EXISTING Linear issues the caller''s key can see (default state=open); the webhook keeps them live thereafter. Synchronous and bounded, idempotent by ExtRef.' operationId: post_provider_linear_issues_backfill requestBody: content: application/json: example: state: all schema: $ref: '#/components/schemas/provider.linearBackfillIn' required: true responses: '200': content: application/json: example: created: 410 failed: 0 issues: 430 updated: 20 schema: $ref: '#/components/schemas/provider.linearBackfillResult' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Seeds the native todo with the EXISTING Linear issues the caller's key can see (default state=open); the webhook keeps them live thereafter. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/linear/webhook: post: description: "The address Linear delivers Issue and Comment events to. An issue event is mirrored into the native todo\ \ \u2014 idempotently by identifier, so ENG-123 is one row however many times it is edited, moved or closed \u2014\ \ and every issue and comment event is handed to the automations engine as a verified trigger, which is how an org\ \ runs an agent when an issue is assigned to it or a comment mentions it. A remove is never propagated: the native\ \ side is canonical.\n\nIt answers a benign 200 for what it does not act on \u2014 an unknown organization, other\ \ event types \u2014 so Linear does not retry-storm. A bad signature and a delivery older than a minute are 401; only\ \ a sink failure is 502.\n\nThe delivery names its Linear organization; that organization's own webhook secret \u2014\ \ sealed at /v1/provider/linear/claim \u2014 verifies the HMAC over the raw body, so the tenant is the organization\ \ the signature proves, never a header.\n\nThe caller here is the PLATFORM, not a Hanzo tenant, so there is no bearer\ \ and no principal. The signature check IS the authentication, and it fails closed. The tenant is never read from\ \ the payload either: it is resolved from the verified platform identifier through the connection map, so an event\ \ from a workspace nobody connected does nothing. Refusals are written with their own status rather than being flattened\ \ to a 500, so a rejected signature reads as 401 and a malformed body as 400." operationId: post_provider_linear_webhook summary: Linear webhook tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/openrouter/webhook: post: description: "OpenRouter's spend is invisible to every Hanzo money lens because those lenses read hanzo.cloud_usage\ \ and OpenRouter meters keys of its own. Point a Broadcast destination (Settings \u25B8 Observability \u25B8 Webhook)\ \ at this endpoint and each generation span becomes ONE row in that same ledger with provider `openrouter`, so one\ \ query answers what we spend everywhere. Enable the Cost and Identity field categories: cost is the money and identity\ \ carries `openrouter.api_key_name`, which is what says WHICH key spent it \u2014 it lands in `account` as openrouter/.\n\nAUTHENTICATION IS A HANZO KEY. Broadcast signs nothing; its only authentication is the destination's Headers\ \ map, so send a key as `Authorization: Bearer pk-\u2026` and it is admitted exactly as /v1/event admits a beacon's:\ \ a project key resolves through the project that minted it, an IAM-issued key through IAM. That key names the org\ \ every row is filed under; it can write and cannot read. No key, or a key that names no org, is 401 and nothing is\ \ stored.\n\nThe body is OTLP/JSON \u2014 `{resourceSpans:[{scopeSpans:[{spans:[\u2026]}]}]}` \u2014 exactly as OpenTelemetry\ \ defines it; the model, tokens and cost are read from each span's `gen_ai.*` attributes and the key name from `openrouter.api_key_name`.\ \ The answer is `{stored, dropped}`: how many generations became rows, and how many spans named no model. Those are\ \ OpenRouter's trace and span parents \u2014 they carry no cost to meter. An empty payload stores nothing and answers\ \ 200, which is what makes Test Connection pass. A warehouse that cannot take the rows answers 503 so the delivery\ \ shows red and can be replayed: a row is keyed by its span id, so a redelivery collapses rather than double-counting." operationId: post_provider_openrouter_webhook requestBody: content: application/json: schema: additionalProperties: {} type: object responses: 2XX: content: application/json: schema: additionalProperties: {} type: object description: Success summary: Receive OpenRouter Broadcast traces as usage rows tags: - provider x-app: provider x-public: true /v1/provider/slack/channels: get: description: 'Lists Slack conversations for the caller''s connected workspace. Read next_cursor until empty to discover channels, including private channels or group DMs when types requests them and the bot has their read scopes.' operationId: get_provider_slack_channels parameters: - description: 'Types selects public_channel, private_channel, im or mpim, comma-separated. Defaults to public_channel; each selected type requires its own read scope.' in: query name: types required: false schema: type: string - description: Cursor continues the previous page's next_cursor. in: query name: cursor required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/provider.slackChannelsOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists Slack conversations for the caller's connected workspace. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/slack/commands: post: description: 'The address Slack posts a slash command to, form-encoded. It acknowledges inside Slack''s three-second budget and posts the answer afterwards to the command''s own response URL, which is why the immediate reply is empty. The body is verified against the same app signing secret as the events webhook, and a repeat of the same command invocation is absorbed rather than answered twice. The caller here is the PLATFORM, not a Hanzo tenant, so there is no bearer and no principal. The signature check IS the authentication, and it fails closed. The tenant is never read from the payload either: it is resolved from the verified platform identifier through the connection map, so an event from a workspace nobody connected does nothing. Refusals are written with their own status rather than being flattened to a 500, so a rejected signature reads as 401 and a malformed body as 400. The answer is acknowledged immediately and the work happens afterwards, because every one of these platforms times out a slow webhook. Duplicate deliveries are absorbed durably, so a platform retry of an event that already ran never runs it a second time or bills for it twice. When the agent pool is full nothing at all is recorded and the delivery is refused as retriable, so the message is re-delivered later rather than being lost or half-processed.' operationId: post_provider_slack_commands summary: Slack slash command webhook tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/slack/events: post: description: "The address a Slack app posts workspace events to. It answers Slack's url_verification handshake with\ \ the challenge, and routes an @mention or a direct message to an agent turn that replies in the same thread. The\ \ turn holds the product's own tools, so a request to change code starts a sandbox run because the model chose to\ \ \u2014 there is no prefix and no second path.\n\nThe raw body and its timestamp are verified against the app's signing\ \ secret before anything is read from them. Hanzo's own bot messages are dropped, so a reply cannot trigger another\ \ reply.\n\nThe caller here is the PLATFORM, not a Hanzo tenant, so there is no bearer and no principal. The signature\ \ check IS the authentication, and it fails closed. The tenant is never read from the payload either: it is resolved\ \ from the verified platform identifier through the connection map, so an event from a workspace nobody connected\ \ does nothing. Refusals are written with their own status rather than being flattened to a 500, so a rejected signature\ \ reads as 401 and a malformed body as 400.\n\nThe answer is acknowledged immediately and the work happens afterwards,\ \ because every one of these platforms times out a slow webhook. Duplicate deliveries are absorbed durably, so a platform\ \ retry of an event that already ran never runs it a second time or bills for it twice. When the agent pool is full\ \ nothing at all is recorded and the delivery is refused as retriable, so the message is re-delivered later rather\ \ than being lost or half-processed." operationId: post_provider_slack_events summary: Slack Events API webhook tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/slack/file: get: description: 'Streams the bytes of a file attached to a message in the workspace this organization connected. `id` is the Slack file id a message''s `files` carries; the response is the file itself, under the Content-Type Slack recorded for it, never JSON. THE WORKSPACE IS THE CALLER''S. The org comes from the validated identity and never from the request, and it selects the token the read spends, so a caller cannot name a workspace they have not connected. A file that workspace cannot see answers 404 from Slack''s own answer rather than from a guess made here, and a file past the size bound is refused by name instead of arriving truncated.' operationId: get_provider_slack_file summary: Read one Slack file's bytes. tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/slack/install: get: description: "The address behind Slack's \"Add to Slack\" and Marketplace Install buttons. It answers a 302 to Slack's\ \ own consent screen and does nothing else \u2014 it is a redirector by design.\n\nIt exists because Slack refuses\ \ a slack.com URL in that field and requires one of ours that redirects there, which makes the field an ATTRIBUTION\ \ hook: routing the click through our own address is what lets an install be counted, and always answering the redirect\ \ is what keeps the counter from becoming a detour that never reaches consent. The destination is the same consent\ \ URL every time, built from the same scopes the console's Connect button asks for, so a workspace is asked to grant\ \ one thing however the install began.\n\nIt is PUBLIC and carries no principal, because whoever clicks Install in\ \ Slack's directory has no Hanzo session yet. It binds no org either, and that is deliberate rather than missing:\ \ the org is resolved at the shared provider callback, from the signed state a console connect minted or from the\ \ workspace's existing connection. Minting an org for an anonymous click is the one thing that would break tenant\ \ isolation, so an install begun here finishes under exactly the rules every other install obeys.\n\nWhere the app\ \ is not configured it answers 503, rather than a consent URL carrying an empty client_id that Slack would render\ \ as its own dead-end error page." operationId: get_provider_slack_install summary: Install the Hanzo app into a Slack workspace tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/slack/join: post: description: 'Joins every public channel in the caller org''s workspace. Org admin, because it changes what the whole workspace sees: after it the agent is a member of every public room and answers in all of them.' operationId: post_provider_slack_join responses: '200': content: application/json: schema: $ref: '#/components/schemas/provider.slackJoinOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Joins every public channel in the caller org's workspace. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/slack/link: get: description: "The entry point behind the connect prompt Hanzo posts in Slack. It starts a link session in the browser\ \ and redirects to Slack's own sign-in, which is what proves which Slack user is asking.\n\nThis is one leg of a three-leg\ \ flow, and the legs are not interchangeable: a browser is expected to arrive here only from the leg before it. The\ \ link URL's state proves the prompt was server-minted and carries the CHAT it started from \u2014 it is provenance\ \ only, and it never decides which account gets linked. The account identity always comes from the platform's own\ \ verified sign-in and a host-bound cookie, so forwarding a link to someone else cannot bind their account, and a\ \ session lifted into another browser is refused rather than completed. Each link is single-use, and a deployment\ \ without linking configured answers 503." operationId: get_provider_slack_link summary: Begin linking a Hanzo account from Slack tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/slack/link/callback: get: description: "The final leg: the user has proved both who they are in Slack and who they are in Hanzo, and this binds\ \ the two. It answers a short confirmation page telling them to return to Slack.\n\nThe Hanzo credential obtained\ \ here is sealed into the connected workspace's own KMS namespace; it is never written to a database column and never\ \ logged. A deployment whose secret store is unavailable refuses the link rather than completing it without custody\ \ of the credential.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is expected\ \ to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and carries\ \ the CHAT it started from \u2014 it is provenance only, and it never decides which account gets linked. The account\ \ identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a link to\ \ someone else cannot bind their account, and a session lifted into another browser is refused rather than completed.\ \ Each link is single-use, and a deployment without linking configured answers 503." operationId: get_provider_slack_link_callback summary: Complete the Slack account link tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/slack/link/slack: get: description: "Where Slack returns the user after they sign in. It establishes the verified Slack workspace and user,\ \ confirms that workspace is connected to an org, and hands the browser on to the Hanzo sign-in that completes the\ \ link.\n\nThe verified pair is carried onward in a host-bound cookie rather than in the URL, so the identity being\ \ linked cannot be edited in transit.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable:\ \ a browser is expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted\ \ and carries the CHAT it started from \u2014 it is provenance only, and it never decides which account gets linked.\ \ The account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding\ \ a link to someone else cannot bind their account, and a session lifted into another browser is refused rather than\ \ completed. Each link is single-use, and a deployment without linking configured answers 503." operationId: get_provider_slack_link_slack summary: Slack sign-in return leg tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/slack/messages: delete: description: 'Takes back one of this app''s messages: DELETE /v1/provider/slack/messages.' operationId: delete_provider_slack_messages parameters: - in: query name: channel required: false schema: type: string - in: query name: ts required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/provider.slackDeleteMessageOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Takes back one of this app''s messages: DELETE /v1/provider/slack/messages.' tags: - provider x-app: provider x-public: true x-tool: true get: description: 'Reads recent messages from a named Slack channel such as #hanzo-gtm. The bot must be a member and have the relevant history scope. Set thread_ts to fetch a parent''s replies, and cursor to continue a page. Channel history is newest first; thread replies are oldest first.' operationId: get_provider_slack_messages parameters: - description: Channel is a Slack channel ID, name (#hanzo-gtm), or Slack channel mention. in: query name: channel required: false schema: type: string - description: Limit bounds the page to 1-100 messages; zero defaults to 15. in: query name: limit required: false schema: format: int64 type: integer - description: Cursor continues the previous page's next_cursor. in: query name: cursor required: false schema: type: string - description: 'ThreadTS reads a thread via conversations.replies. Empty reads recent channel messages via conversations.history, which excludes thread replies.' in: query name: thread_ts required: false schema: type: string - description: Oldest filters to messages after this Slack timestamp (exclusive). in: query name: oldest required: false schema: type: string - description: Latest filters to messages before this Slack timestamp (exclusive). in: query name: latest required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/provider.slackMessagesOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Reads recent messages from a named Slack channel such as #hanzo-gtm.' tags: - provider x-app: provider x-public: true x-tool: true post: description: 'Posts as Hanzo to the caller''s connected Slack workspace. Use channel from a message read and thread_ts to reply to a specific message. Requires chat:write; this operation does not join channels automatically.' operationId: post_provider_slack_messages requestBody: content: application/json: schema: $ref: '#/components/schemas/provider.slackSendMessageIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/provider.slackSendMessageOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Posts as Hanzo to the caller's connected Slack workspace. tags: - provider x-app: provider x-public: true x-tool: true put: description: 'Rewrites one of this app''s messages: PUT /v1/provider/slack/messages. Slack refuses a message the token did not post, and that refusal is reported as it arrives.' operationId: put_provider_slack_messages requestBody: content: application/json: schema: $ref: '#/components/schemas/provider.slackUpdateMessageIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/provider.slackUpdateMessageOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Rewrites one of this app''s messages: PUT /v1/provider/slack/messages.' tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/slack/reactions: post: description: 'Adds an emoji reaction: POST /v1/provider/slack/reactions.' operationId: post_provider_slack_reactions requestBody: content: application/json: schema: $ref: '#/components/schemas/provider.slackReactIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/provider.slackReactOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Adds an emoji reaction: POST /v1/provider/slack/reactions.' tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/slack/search: post: description: 'Answers a workspace question: POST /v1/provider/slack/search. Slack''s search.messages wants a USER token for a person''s own view of a workspace; a bot token searches what the app can see. Whichever the install granted, the token custodied for this org is the one sent.' operationId: post_provider_slack_search requestBody: content: application/json: schema: $ref: '#/components/schemas/provider.slackSearchIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/provider.slackSearchOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Answers a workspace question: POST /v1/provider/slack/search.' tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/teams/events: post: description: 'The messaging endpoint for the Teams bot. A message activity is routed to an agent turn and answered proactively through the Bot Connection; anything that is not a message with text is acknowledged and ignored. Authentication is the Bot Framework''s RS256 JWT, verified against its published keys and bound BOTH to this deployment''s app id and to the activity''s own service URL. The service-URL binding is the part that matters: without it a token valid for one activity could point the outbound reply somewhere else. The caller here is the PLATFORM, not a Hanzo tenant, so there is no bearer and no principal. The signature check IS the authentication, and it fails closed. The tenant is never read from the payload either: it is resolved from the verified platform identifier through the connection map, so an event from a workspace nobody connected does nothing. Refusals are written with their own status rather than being flattened to a 500, so a rejected signature reads as 401 and a malformed body as 400. The answer is acknowledged immediately and the work happens afterwards, because every one of these platforms times out a slow webhook. Duplicate deliveries are absorbed durably, so a platform retry of an event that already ran never runs it a second time or bills for it twice. When the agent pool is full nothing at all is recorded and the delivery is refused as retriable, so the message is re-delivered later rather than being lost or half-processed.' operationId: post_provider_teams_events summary: Microsoft Teams Bot Framework webhook tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/teams/link: get: description: "The entry point behind the connect prompt Hanzo shows in Teams. It starts a link session and redirects\ \ to Microsoft sign-in addressed to the CHAT'S OWN tenant, not the common endpoint, so only a member of that tenant\ \ can complete it.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is expected\ \ to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and carries\ \ the CHAT it started from \u2014 it is provenance only, and it never decides which account gets linked. The account\ \ identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a link to\ \ someone else cannot bind their account, and a session lifted into another browser is refused rather than completed.\ \ Each link is single-use, and a deployment without linking configured answers 503." operationId: get_provider_teams_link summary: Begin linking a Hanzo account from Teams tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/teams/link/aad: get: description: "Where Microsoft returns the user after sign-in. It resolves the verified directory identity and then re-checks\ \ the tenant: the signed-in user's tenant must equal the tenant of the chat the link started from, so a valid Microsoft\ \ sign-in from a different organization is refused here rather than accepted.\n\nThis is the leg Teams has and the\ \ other platforms do not, which is why the Teams flow has an extra address.\n\nThis is one leg of a three-leg flow,\ \ and the legs are not interchangeable: a browser is expected to arrive here only from the leg before it. The link\ \ URL's state proves the prompt was server-minted and carries the CHAT it started from \u2014 it is provenance only,\ \ and it never decides which account gets linked. The account identity always comes from the platform's own verified\ \ sign-in and a host-bound cookie, so forwarding a link to someone else cannot bind their account, and a session lifted\ \ into another browser is refused rather than completed. Each link is single-use, and a deployment without linking\ \ configured answers 503." operationId: get_provider_teams_link_aad summary: Microsoft sign-in return leg tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/teams/link/callback: get: description: "The final leg: it binds the verified directory identity to the Hanzo account that just signed in, and\ \ answers a short confirmation page telling them to return to Teams. The Hanzo credential is sealed into the connected\ \ org's KMS namespace.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is\ \ expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and\ \ carries the CHAT it started from \u2014 it is provenance only, and it never decides which account gets linked. The\ \ account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a\ \ link to someone else cannot bind their account, and a session lifted into another browser is refused rather than\ \ completed. Each link is single-use, and a deployment without linking configured answers 503." operationId: get_provider_teams_link_callback summary: Complete the Teams account link tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/telegram/connect: post: description: "Mints a short, single-use deep-link code bound to the caller's\norg and returns the t.me link the console\ \ navigates to. Org-authed: a caller with\nno validated principal is 403 (same gate as the framework connect). The\ \ code is\nstored as an oauth_nonce (org,telegram); the webhook's /start handler claims it to\nbind chat\u2192org.\ \ It is short (128-bit hex) so it fits Telegram's 64-char `start`\npayload limit." operationId: post_provider_telegram_connect responses: '200': content: application/json: example: authorizeUrl: https://t.me/hanzo_bot?start=9f3c1d2e4b5a6c7d8e9f0a1b2c3d4e5f schema: $ref: '#/components/schemas/provider.authorizeOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Mints a short, single-use deep-link code bound to the caller's org and returns the t.me link the console navigates to. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/telegram/link: get: description: "The entry point behind the connect prompt Hanzo sends in Telegram. Unlike the other platforms it answers\ \ an HTML PAGE rather than a redirect: Telegram has no OAuth flow, so the page hosts Telegram's Login Widget, and\ \ the browser is sent onward only after the user signs in through it.\n\nThe widget only appears on the domain registered\ \ for the bot, so a deployment whose bot domain is unset renders a page with nothing on it.\n\nThis is one leg of\ \ a three-leg flow, and the legs are not interchangeable: a browser is expected to arrive here only from the leg before\ \ it. The link URL's state proves the prompt was server-minted and carries the CHAT it started from \u2014 it is provenance\ \ only, and it never decides which account gets linked. The account identity always comes from the platform's own\ \ verified sign-in and a host-bound cookie, so forwarding a link to someone else cannot bind their account, and a\ \ session lifted into another browser is refused rather than completed. Each link is single-use, and a deployment\ \ without linking configured answers 503." operationId: get_provider_telegram_link summary: Begin linking a Hanzo account from Telegram tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/telegram/link/auth: get: description: "Where Telegram's Login Widget sends the user with its signed authentication data. That data is verified\ \ against the bot token \u2014 this is the identity source, and it is the widget's signature rather than a code exchange\ \ \u2014 and the chat is confirmed to be bound to an org before the browser is handed to the Hanzo sign-in.\n\nWidget\ \ data is only accepted while it is fresh, so a captured sign-in blob cannot be replayed later even though its signature\ \ stays valid.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is expected\ \ to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and carries\ \ the CHAT it started from \u2014 it is provenance only, and it never decides which account gets linked. The account\ \ identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a link to\ \ someone else cannot bind their account, and a session lifted into another browser is refused rather than completed.\ \ Each link is single-use, and a deployment without linking configured answers 503." operationId: get_provider_telegram_link_auth summary: Telegram Login Widget return leg tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/telegram/link/callback: get: description: "The final leg: it binds the verified Telegram user to the Hanzo account that just signed in, and answers\ \ a short confirmation page telling them to return to Telegram. The Hanzo credential is sealed into the connected\ \ org's KMS namespace.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is\ \ expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and\ \ carries the CHAT it started from \u2014 it is provenance only, and it never decides which account gets linked. The\ \ account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a\ \ link to someone else cannot bind their account, and a session lifted into another browser is refused rather than\ \ completed. Each link is single-use, and a deployment without linking configured answers 503." operationId: get_provider_telegram_link_callback summary: Complete the Telegram account link tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/telegram/webhook: post: description: 'The update webhook for the Telegram bot. It does two jobs: `/start ` or `/connect ` binds the chat it was sent from to an org, idempotently; anything else is treated as a possible agent trigger. What counts as a trigger differs by chat type, and it is easy to get wrong: in a private chat every message is a trigger, while in a group the message must mention the bot or use the `/hanzo` command. Non-triggers and non-message updates are acknowledged and dropped. Authentication is the secret token Telegram echoes on every update, compared in constant time. A message in a chat that has never been bound is dropped, which is why the bind command exists. The caller here is the PLATFORM, not a Hanzo tenant, so there is no bearer and no principal. The signature check IS the authentication, and it fails closed. The tenant is never read from the payload either: it is resolved from the verified platform identifier through the connection map, so an event from a workspace nobody connected does nothing. Refusals are written with their own status rather than being flattened to a 500, so a rejected signature reads as 401 and a malformed body as 400. The answer is acknowledged immediately and the work happens afterwards, because every one of these platforms times out a slow webhook. Duplicate deliveries are absorbed durably, so a platform retry of an event that already ran never runs it a second time or bills for it twice. When the agent pool is full nothing at all is recorded and the delivery is refused as retriable, so the message is re-delivered later rather than being lost or half-processed.' operationId: post_provider_telegram_webhook summary: Telegram Bot API webhook tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/whatsapp/webhook: get: description: "Meta calls this once when the webhook is subscribed, carrying the verify token this deployment was configured\ \ with and a challenge to echo. The token is compared in constant time before the echo \u2014 answering the challenge\ \ without checking it would let anyone point their own app at this address and have it confirm the subscription." operationId: get_provider_whatsapp_webhook summary: WhatsApp Cloud API subscription challenge tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). post: description: "One delivery from Meta. Authenticity is the X-Hub-Signature-256 HMAC over the raw body, and it is the\ \ whole of it: a message accepted here creates the reply route that authorises this org to answer, so an unsigned\ \ delivery would let anyone hand an org a conversation to answer under its own number.\n\nMeta batches (entry \xD7\ \ changes \xD7 messages) and sends status callbacks \u2014 sent/delivered/read \u2014 through this same address with\ \ no message at all. Those are acknowledged and dropped rather than refused, because a non-2xx is retried with backoff\ \ and eventually disables the subscription: the only refusals here are an unconfigured endpoint and a bad signature,\ \ which are ours to fix and not Meta's to retry.\n\nThe answer is acknowledged immediately and the work happens afterwards,\ \ because every one of these platforms times out a slow webhook. Duplicate deliveries are absorbed durably, so a platform\ \ retry of an event that already ran never runs it a second time or bills for it twice. When the agent pool is full\ \ nothing at all is recorded and the delivery is refused as retriable, so the message is re-delivered later rather\ \ than being lost or half-processed." operationId: post_provider_whatsapp_webhook summary: WhatsApp Cloud API webhook tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/{provider}: get: description: "Returns ONE provider with this org's connection status \u2014 the same view list\ncarries, for a single\ \ id. An unknown id is 404, and so is a user-plane provider:\nthe org surface never resolves one." operationId: get_provider_by_provider parameters: - description: "Provider is the registry id of the connector \u2014 \"slack\", \"github\",\n\"cloudflare\". Unknown\ \ ids are 404, as are the user-plane (/v1/connection)\nproviders, which this surface never resolves." example: slack in: path name: provider required: true schema: type: string responses: '200': content: application/json: example: available: true category: Communication connected: false description: Connect your workspace. id: slack name: Slack schema: $ref: '#/components/schemas/provider.providerView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns ONE provider with this org's connection status \u2014 the same view list carries, for a single id." tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/{provider}/callback: get: description: "The single address every connector's OAuth flow returns to. It exchanges the authorization the provider\ \ granted, records the connection, and ALWAYS redirects the browser back to the console \u2014 on success and on every\ \ labeled failure alike, so a user never lands on a raw JSON dead end.\n\nIt is public and carries no principal, so\ \ the org is taken ONLY from the signed state minted when the flow began; no header is trusted here. That state is\ \ single-use and is burned BEFORE the exchange, so one authorization is one attempt and a replayed return fails instead\ \ of exchanging twice.\n\nTokens are sealed into the org's KMS namespace BEFORE the connection row is written, so\ \ a failure of the secret store leaves no half-connected integration advertising a credential that was never stored.\ \ Token values never appear in the redirect, in a log line or in an error.\n\nOne generalization is worth knowing:\ \ a GitHub App installation returns an installation identifier instead of an OAuth code, and it is accepted in the\ \ code's place so the App model needs no second address." operationId: get_provider_by_provider_callback parameters: - in: path name: provider required: true schema: type: string summary: OAuth return for any connector tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/{provider}/connect: post: description: "Acquires the org's credential for one provider. It has TWO paths and the\nREQUEST picks which: a \"token\"\ \ key in the body seals that credential directly\n(verify-before-store), and its absence begins the 3-legged OAuth\ \ flow \u2014 minting a\nsingle-use nonce plus an HMAC-signed state that binds this org to this provider,\nand answering\ \ with the provider's authorize URL for the caller to redirect to.\n\nFail-closed order, unchanged: no principal \u2192\ \ 403; unknown provider \u2192 404; an\nAdminOnly connector without the caller's own-org admin bit \u2192 403; not\ \ configured\n\u2192 503; KMS not ready \u2192 503 (the flow WILL need to seal a token, so refuse now\nrather than\ \ dead-end at the callback)." operationId: post_provider_by_provider_connect parameters: - description: Provider is the connector's registry id, from the :provider path segment. example: cloudflare in: path name: provider required: true schema: type: string requestBody: content: application/json: example: accountId: a1b2c3 provider: cloudflare token: cf-scoped-api-token schema: $ref: '#/components/schemas/provider.connectIn' required: true responses: '200': content: application/json: example: account: Acme connected: true externalId: a1b2c3 provider: cloudflare scopes: [] schema: $ref: '#/components/schemas/provider.connectOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Acquires the org's credential for one provider. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/{provider}/disconnect: post: description: "Revokes (best-effort) and forgets an org's connection: it deletes\nevery custodied KMS secret and the\ \ connection row. Idempotent \u2014 disconnecting a\nprovider that was never connected still returns {disconnected:true}.\ \ Symmetric\nwith connect: an AdminOnly connector needs the caller's own-org admin bit." operationId: post_provider_by_provider_disconnect parameters: - description: "Provider is the registry id of the connector \u2014 \"slack\", \"github\",\n\"cloudflare\". Unknown\ \ ids are 404, as are the user-plane (/v1/connection)\nproviders, which this surface never resolves." example: slack in: path name: provider required: true schema: type: string responses: '200': content: application/json: example: disconnected: true schema: $ref: '#/components/schemas/provider.disconnectOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Revokes (best-effort) and forgets an org''s connection: it deletes every custodied KMS secret and the connection row.' tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/{provider}/logo: get: description: "Serves a connector's logo from the catalog. PUBLIC: an carries no\nbearer, and a logo is not the\ \ org's. It is served from the embedded catalog\nonly, never fetched, with a policy that runs nothing \u2014 an SVG\ \ opened directly\nis a picture, not a page." operationId: get_provider_by_provider_logo parameters: - in: path name: provider required: true schema: type: string summary: Serves a connector's logo from the catalog. tags: - provider x-app: provider x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/provider/{provider}/run: post: description: 'Runs one action of a connector as the caller''s org, with the credential the org connected, and answers what the action returned. The credential is read from the org''s KMS namespace here and never leaves this service. An action that ran and failed is ok:false with the reason; an unknown connector is 404, an action that does not run on this deployment 422, and a connector the org has not connected 424.' operationId: post_provider_by_provider_run parameters: - description: Provider is the connector, from the path. example: bitly in: path name: provider required: true schema: type: string requestBody: content: application/json: example: action: create_bitlink input: long_url: https://hanzo.ai provider: bitly schema: $ref: '#/components/schemas/provider.runIn' required: true responses: '200': content: application/json: example: ok: true output: link: https://bit.ly/3xyz schema: $ref: '#/components/schemas/provider.runOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Runs one action of a connector as the caller's org, with the credential the org connected, and answers what the action returned. tags: - provider x-app: provider x-public: true x-tool: true /v1/provider/{provider}/verify: post: description: "Re-checks a CONNECTED apikey connector's stored credential against the\nprovider, live (`hanzo connector\ \ verify`). Org-scoped (any member may check\nstatus); the credential is read from KMS, verified, and NEVER returned\ \ or logged.\nA verification failure is reported as {active:false}, not an error \u2014 the console/\nCLI renders\ \ it. Only apikey providers support verify (OAuth tokens are checked at\nuse, not re-verified here)." operationId: post_provider_by_provider_verify parameters: - description: "Provider is the registry id of the connector \u2014 \"slack\", \"github\",\n\"cloudflare\". Unknown\ \ ids are 404, as are the user-plane (/v1/connection)\nproviders, which this surface never resolves." example: cloudflare in: path name: provider required: true schema: type: string responses: '200': content: application/json: example: account: Acme active: true externalId: a1b2c3 provider: cloudflare scopes: - zone:read schema: $ref: '#/components/schemas/provider.verifyOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Re-checks a CONNECTED apikey connector's stored credential against the provider, live (`hanzo connector verify`). tags: - provider x-app: provider x-public: true x-tool: true /v1/provisioning/datastore: get: description: 'Lists the caller org''s Hanzo Datastore warehouses. Each one is a DEDICATED analytical instance the org alone runs, so the host is that instance''s own in-cluster Service and the port is its HTTP port, 8123.' operationId: get_provisioning_datastore responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/provisioning.provisionedSummary' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller org's Hanzo Datastore warehouses. tags: - provisioning x-app: provisioning x-public: true x-tool: true post: description: "Launches your org's OWN Hanzo Datastore instance and answers\nwith its `datastore://` connection string.\n\ \nThe instance is yours alone \u2014 a deployment in your own tenant namespace, so its\nadmin credential is naturally\ \ scoped to you and no other tenant shares the\nprocess. Off-cluster this fails closed with 503 rather than handing\ \ back a\nshared one." operationId: post_provisioning_datastore requestBody: content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionRequest' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionResult' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Launches your org's OWN Hanzo Datastore instance and answers with its `datastore://` connection string. tags: - provisioning x-app: provisioning x-public: true x-tool: true /v1/provisioning/datastore/{name}: delete: description: 'Deprovisions one Hanzo Datastore warehouse. It reverts any app instance bound to it back to Base BEFORE tearing down the org''s dedicated instance, then deletes the sealed credential and removes the metadata row. Answers 204 with no body; a second call is a 404.' operationId: delete_provisioning_datastore_by_name parameters: - description: 'Name is the resource''s org-unique slug, from the path. Lower-cased and trimmed before lookup, exactly as it was at create.' example: warehouse in: path name: name required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deprovisions one Hanzo Datastore warehouse. tags: - provisioning x-app: provisioning x-public: true x-tool: true get: description: "Returns one Hanzo Datastore warehouse's metadata. It carries the\nwarehouse's status, its instance address\ \ and the admin user the instance\nbooted with \u2014 never the password. A still-booting instance reads\n\"provisioning\"\ , reconciled from the operator's live view rather than the row." operationId: get_provisioning_datastore_by_name parameters: - description: 'Name is the resource''s org-unique slug, from the path. Lower-cased and trimmed before lookup, exactly as it was at create.' example: warehouse in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionedResource' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one Hanzo Datastore warehouse's metadata. tags: - provisioning x-app: provisioning x-public: true x-tool: true /v1/provisioning/docdb: get: description: 'Lists the caller org''s Hanzo DocDB document databases. Each one is a DEDICATED FerretDB instance the org alone runs, speaking the MongoDB wire protocol, so the host is that instance''s own in-cluster Service and the port is 27017.' operationId: get_provisioning_docdb responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/provisioning.provisionedSummary' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller org's Hanzo DocDB document databases. tags: - provisioning x-app: provisioning x-public: true x-tool: true post: description: "Launches your org's OWN document-database instance and answers with\nits `mongodb://` connection string.\ \ It speaks the MongoDB wire protocol, so\nexisting MongoDB drivers connect unchanged.\n\nThe instance is yours alone\ \ \u2014 a deployment in your own tenant namespace, so its\nadmin credential is naturally scoped to you and no other\ \ tenant shares the\nprocess. Off-cluster this fails closed with 503 rather than handing back a\nshared one." operationId: post_provisioning_docdb requestBody: content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionRequest' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionResult' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Launches your org's OWN document-database instance and answers with its `mongodb://` connection string. tags: - provisioning x-app: provisioning x-public: true x-tool: true /v1/provisioning/docdb/{name}: delete: description: 'Deprovisions one Hanzo DocDB database. It reverts any app instance bound to it back to Base BEFORE tearing down the org''s dedicated FerretDB instance, then deletes the sealed credential and removes the metadata row. Answers 204 with no body; a second call is a 404.' operationId: delete_provisioning_docdb_by_name parameters: - description: 'Name is the resource''s org-unique slug, from the path. Lower-cased and trimmed before lookup, exactly as it was at create.' example: sessions in: path name: name required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deprovisions one Hanzo DocDB database. tags: - provisioning x-app: provisioning x-public: true x-tool: true get: description: "Returns one Hanzo DocDB database's metadata. It carries the\ndatabase's status, its instance address and\ \ the SCRAM user the instance was\nset up with \u2014 never the password. A still-booting instance reads\n\"provisioning\"\ , reconciled from the operator's live view." operationId: get_provisioning_docdb_by_name parameters: - description: 'Name is the resource''s org-unique slug, from the path. Lower-cased and trimmed before lookup, exactly as it was at create.' example: sessions in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionedResource' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one Hanzo DocDB database's metadata. tags: - provisioning x-app: provisioning x-public: true x-tool: true /v1/provisioning/kv: get: description: 'Lists the caller org''s Hanzo KV stores. Each one is a DEDICATED Valkey instance the org alone runs, so the host is that instance''s own in-cluster Service and the port is 6379.' operationId: get_provisioning_kv responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/provisioning.provisionedSummary' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller org's Hanzo KV stores. tags: - provisioning x-app: provisioning x-public: true x-tool: true post: description: "Launches your org's OWN key-value instance and answers with its `kv://`\nconnection string.\n\nThe instance\ \ is yours alone \u2014 a deployment in your own tenant namespace, so its\nadmin credential is naturally scoped to\ \ you and no other tenant shares the\nprocess. Off-cluster this fails closed with 503 rather than handing back a\n\ shared one." operationId: post_provisioning_kv requestBody: content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionRequest' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionResult' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Launches your org's OWN key-value instance and answers with its `kv://` connection string. tags: - provisioning x-app: provisioning x-public: true x-tool: true /v1/provisioning/kv/{name}: delete: description: 'Deprovisions one Hanzo KV store. It reverts any app instance bound to it back to Base BEFORE tearing down the org''s dedicated Valkey instance, then deletes the sealed credential and removes the metadata row. Answers 204 with no body; a second call is a 404.' operationId: delete_provisioning_kv_by_name parameters: - description: 'Name is the resource''s org-unique slug, from the path. Lower-cased and trimmed before lookup, exactly as it was at create.' example: sessions in: path name: name required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deprovisions one Hanzo KV store. tags: - provisioning x-app: provisioning x-public: true x-tool: true get: description: "Returns one Hanzo KV store's metadata. It carries the store's status,\nits instance address and the Valkey\ \ user it authenticates as (\"default\", the\nonly user a requirepass instance has) \u2014 never the password. A still-booting\n\ instance reads \"provisioning\", reconciled from the operator's live view." operationId: get_provisioning_kv_by_name parameters: - description: 'Name is the resource''s org-unique slug, from the path. Lower-cased and trimmed before lookup, exactly as it was at create.' example: sessions in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionedResource' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one Hanzo KV store's metadata. tags: - provisioning x-app: provisioning x-public: true x-tool: true /v1/provisioning/s3: get: description: 'Lists the caller org''s object-storage buckets. A bucket lives in an already-live shared object store and is reached through the public gateway. The names here are the friendly ones the org provisioned; the physical bucket is org-namespaced underneath, which is what keeps two tenants'' buckets distinct.' operationId: get_provisioning_s3 responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/provisioning.provisionedSummary' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller org's object-storage buckets. tags: - provisioning x-app: provisioning x-public: true x-tool: true post: description: 'Creates an S3-compatible bucket inside the already-running shared object store and answers with the endpoint that reaches it.' operationId: post_provisioning_s3 requestBody: content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionRequest' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionResult' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates an S3-compatible bucket inside the already-running shared object store and answers with the endpoint that reaches it. tags: - provisioning x-app: provisioning x-public: true x-tool: true /v1/provisioning/s3/{name}: delete: description: 'Deletes one bucket from the shared object store and removes its metadata row. Answers 204 with no body; a second call is a 404.' operationId: delete_provisioning_s3_by_name parameters: - description: 'Name is the resource''s org-unique slug, from the path. Lower-cased and trimmed before lookup, exactly as it was at create.' example: uploads in: path name: name required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes one bucket from the shared object store and removes its metadata row. tags: - provisioning x-app: provisioning x-public: true x-tool: true get: description: 'Returns one bucket''s metadata. It carries the bucket''s status and the gateway address it is reached at, and no username: the object store authenticates with a shared, out-of-band key rather than a per-bucket credential.' operationId: get_provisioning_s3_by_name parameters: - description: 'Name is the resource''s org-unique slug, from the path. Lower-cased and trimmed before lookup, exactly as it was at create.' example: uploads in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionedResource' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one bucket's metadata. tags: - provisioning x-app: provisioning x-public: true x-tool: true /v1/provisioning/search: get: description: 'Lists the caller org''s search indexes. An index is a logical resource inside an already-live shared backend, so every one of them is reached through the public gateway rather than at an instance of its own.' operationId: get_provisioning_search responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/provisioning.provisionedSummary' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller org's search indexes. tags: - provisioning x-app: provisioning x-public: true x-tool: true post: description: 'Creates a search index inside the already-running shared search backend and answers with the endpoint that reaches it.' operationId: post_provisioning_search requestBody: content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionRequest' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionResult' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a search index inside the already-running shared search backend and answers with the endpoint that reaches it. tags: - provisioning x-app: provisioning x-public: true x-tool: true /v1/provisioning/search/{name}: delete: description: 'Deletes one search index from the shared backend and removes its metadata row. Answers 204 with no body; a second call is a 404.' operationId: delete_provisioning_search_by_name parameters: - description: 'Name is the resource''s org-unique slug, from the path. Lower-cased and trimmed before lookup, exactly as it was at create.' example: products in: path name: name required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes one search index from the shared backend and removes its metadata row. tags: - provisioning x-app: provisioning x-public: true x-tool: true get: description: 'Returns one search index''s metadata. It carries the index''s status and the gateway address it is reached at, and no username: the backend authenticates with a shared, out-of-band key rather than a per-index credential.' operationId: get_provisioning_search_by_name parameters: - description: 'Name is the resource''s org-unique slug, from the path. Lower-cased and trimmed before lookup, exactly as it was at create.' example: products in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionedResource' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one search index's metadata. tags: - provisioning x-app: provisioning x-public: true x-tool: true /v1/provisioning/sql: get: description: 'Lists the caller org''s Hanzo SQL databases. Each one is a DEDICATED PostgreSQL instance the org alone runs, so the host is that instance''s own in-cluster Service and the port is 5432.' operationId: get_provisioning_sql responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/provisioning.provisionedSummary' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller org's Hanzo SQL databases. tags: - provisioning x-app: provisioning x-public: true x-tool: true post: description: "Launches your org's OWN PostgreSQL instance and answers with its\n`postgres://` connection string.\n\n\ The instance is yours alone \u2014 a deployment in your own tenant namespace, so its\nadmin credential is naturally\ \ scoped to you and no other tenant shares the\nprocess. Off-cluster, where there is no orchestrator to launch one,\ \ this fails\nclosed with 503 rather than handing back a shared one." operationId: post_provisioning_sql requestBody: content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionRequest' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionResult' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Launches your org's OWN PostgreSQL instance and answers with its `postgres://` connection string. tags: - provisioning x-app: provisioning x-public: true x-tool: true /v1/provisioning/sql/{name}: delete: description: "Deprovisions one Hanzo SQL database. It reverts any app instance\nbound to it back to Base BEFORE tearing\ \ down the org's dedicated Postgres\ninstance \u2014 never a live app pointed at a deleted backend \u2014 then deletes\ \ the\nsealed credential and removes the metadata row. Answers 204 with no body; a\nsecond call is a 404, not a second\ \ delete." operationId: delete_provisioning_sql_by_name parameters: - description: 'Name is the resource''s org-unique slug, from the path. Lower-cased and trimmed before lookup, exactly as it was at create.' example: orders in: path name: name required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deprovisions one Hanzo SQL database. tags: - provisioning x-app: provisioning x-public: true x-tool: true get: description: "Returns one Hanzo SQL database's metadata. It carries the database's\nstatus, its instance address and\ \ the admin user Postgres booted with \u2014 never\nthe password, which is returned once at create and otherwise lives\ \ only in\nHanzo KMS. A still-booting instance reads \"provisioning\", reconciled from the\noperator's live view rather\ \ than from the row." operationId: get_provisioning_sql_by_name parameters: - description: 'Name is the resource''s org-unique slug, from the path. Lower-cased and trimmed before lookup, exactly as it was at create.' example: orders in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionedResource' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one Hanzo SQL database's metadata. tags: - provisioning x-app: provisioning x-public: true x-tool: true /v1/provisioning/vector: get: description: 'Lists the caller org''s vector collections. A collection is a logical resource inside an already-live shared backend, so every one of them is reached through the public gateway rather than at an instance of its own.' operationId: get_provisioning_vector responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/provisioning.provisionedSummary' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller org's vector collections. tags: - provisioning x-app: provisioning x-public: true x-tool: true post: description: 'Creates a vector collection inside the already-running shared vector backend and answers with the endpoint that reaches it.' operationId: post_provisioning_vector requestBody: content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionRequest' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionResult' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a vector collection inside the already-running shared vector backend and answers with the endpoint that reaches it. tags: - provisioning x-app: provisioning x-public: true x-tool: true /v1/provisioning/vector/{name}: delete: description: 'Deletes one vector collection from the shared backend and removes its metadata row. Answers 204 with no body; a second call is a 404.' operationId: delete_provisioning_vector_by_name parameters: - description: 'Name is the resource''s org-unique slug, from the path. Lower-cased and trimmed before lookup, exactly as it was at create.' example: embeddings in: path name: name required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes one vector collection from the shared backend and removes its metadata row. tags: - provisioning x-app: provisioning x-public: true x-tool: true get: description: 'Returns one vector collection''s metadata. It carries the collection''s status and the gateway address it is reached at, and no username: the backend authenticates with a shared, out-of-band key rather than a per-collection credential, so there is no per-resource user to report.' operationId: get_provisioning_vector_by_name parameters: - description: 'Name is the resource''s org-unique slug, from the path. Lower-cased and trimmed before lookup, exactly as it was at create.' example: embeddings in: path name: name required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/provisioning.provisionedResource' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one vector collection's metadata. tags: - provisioning x-app: provisioning x-public: true x-tool: true /v1/pubsub/publish: post: description: "Puts one message on the org's bus. When a stream captures the subject\nthe write is DURABLE \u2014 the\ \ receipt names the stream and sequence only after\nJetStream has it on storage, and a repeated Nats-Msg-Id header\ \ within the\ndedup window answers duplicate instead of storing twice. When nothing\ncaptures it, the message goes\ \ out core NATS: delivered to current\nsubscribers, receipt {ok}, nothing retained." operationId: post_pubsub_publish requestBody: content: application/json: example: data: '{"id":"o_1"}' headers: Nats-Msg-Id: o_1 subject: orders.created schema: $ref: '#/components/schemas/pubsub.busPublish' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/pubsub.busAck' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Puts one message on the org's bus. tags: - pubsub x-app: pubsub x-public: true x-tool: true /v1/pubsub/request: post: description: "Sends one request on the org's bus and waits for one reply \u2014 the\nsynchronous half of pub/sub, for\ \ callers speaking to a responder subscribed\non the NATS port. 404 when nobody is listening on the subject; 408 when\ \ a\nresponder exists but no reply arrived within the timeout." operationId: post_pubsub_request requestBody: content: application/json: example: data: '{"sku":"gpu_1"}' subject: billing.quote timeoutMs: 2000 schema: $ref: '#/components/schemas/pubsub.busRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/pubsub.busMessage' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Sends one request on the org's bus and waits for one reply \u2014 the synchronous half of pub/sub, for callers\ \ speaking to a responder subscribed on the NATS port." tags: - pubsub x-app: pubsub x-public: true x-tool: true /v1/reference: get: description: "Lists every set this plane publishes, with its version and how\nfresh it is.\n\nRead the Stale and Refused\ \ lists first: they are the two ways this plane can\nbe quietly wrong, and they are reported rather than inferred.\ \ A set in\nRefused answers nothing \u2014 it has never loaded, it is held by another\ncomponent, or it names a source\ \ we hold no licence for." operationId: riskReferenceSets responses: '200': content: application/json: schema: $ref: '#/components/schemas/reference.ReferenceSetsOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists every set this plane publishes, with its version and how fresh it is. tags: - reference x-app: reference x-public: true x-tool: true /v1/reference/refresh: post: description: 'Takes a new version of one set. SuperAdmin only. It is platform work, not tenant work: it writes the shared baseline every organisation reads, so it is gated to the platform''s own identity. Nothing here can write an organisation''s overrides, and nothing an organisation sends can reach this route. Idempotent. A version is the content digest of what was taken, so refreshing an unchanged publisher writes no rows and reports unchanged. Resumable: a run that died half-way is continued from where it stopped rather than restarted. A set whose source needs a licence we do not hold is refused with the reason, rather than being quietly skipped.' operationId: riskRefreshReference requestBody: content: application/json: example: set: domain schema: $ref: '#/components/schemas/reference.RefreshReferenceIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/reference.RefreshReferenceOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Takes a new version of one set. tags: - reference x-app: reference x-public: true x-tool: true /v1/reference/resolve: post: description: "Looks keys up against the reference plane.\n\nYour organisation's own overrides are consulted FIRST and\ \ win outright; the\nshared baseline answers everything they do not cover. Every answer names the\nversion that produced\ \ it, when that version was current and whether it is\nstale, so a decision can record exactly what it consulted.\n\ \nRead Refusal before reading Hit. A set that has never loaded, one held by the\ncomponent that screens against it,\ \ and one whose source needs a licence we do\nnot hold all answer with a refusal \u2014 and a miss on a refusing set\ \ means\nnothing is known, not that the key is clean." operationId: riskResolveReference requestBody: content: application/json: example: keys: - user@tempbox.example - 3.5.140.1 sets: - domain - net schema: $ref: '#/components/schemas/reference.ResolveReferenceIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/reference.ResolveReferenceOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Looks keys up against the reference plane. tags: - reference x-app: reference x-public: true x-tool: true /v1/reference/{set}: delete: description: 'Removes one of your organisation''s overrides. It removes an entry your organisation wrote, never a baseline member: the published set is not writable from here, so a removal can only ever restore the baseline''s own answer.' operationId: riskClearReference parameters: - example: domain in: path name: set required: true schema: type: string - example: partner.example in: query name: key required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/reference.ClearReferenceOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one of your organisation's overrides. tags: - reference x-app: reference x-public: true x-tool: true get: description: "Reference describes one set and lists your org's overrides in it.\n\nThe set half is public data about\ \ a published list \u2014 its version, its\npublishers, their licences and how current each one is. The overrides\ \ half is\nyours alone: it is read from your organisation's own store, and no other\norganisation's entries can appear\ \ in it." operationId: riskReference parameters: - example: domain in: path name: set required: true schema: type: string - description: 'After pages the override listing: the last key of the previous page.' in: query name: after required: false schema: type: string - description: 'Limit caps the override listing: default 200, maximum 1000.' example: 50 in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/reference.ReferenceOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reference describes one set and lists your org's overrides in it. tags: - reference x-app: reference x-public: true x-tool: true put: description: "Writes your organisation's own allow and deny entries over a set.\n\nIdempotent on the key: writing the\ \ same entry twice is one entry, and writing\nit again replaces the verdict and the note. The whole batch is one\n\ transaction, so a batch that would cross the per-set bound writes nothing\nrather than half of itself \u2014 a half-applied\ \ deny list is worse than a refused\none, because nobody can tell which half applied.\n\nYour entries are held in\ \ your organisation's own store and are never visible\nto another organisation, and they never change what any other\ \ organisation\nsees. The shared baseline is not writable from here at all." operationId: riskSetReference parameters: - example: domain in: path name: set required: true schema: type: string requestBody: content: application/json: example: entries: - key: partner.example note: our reseller verdict: allow set: domain schema: $ref: '#/components/schemas/reference.SetReferenceIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/reference.SetReferenceOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Writes your organisation's own allow and deny entries over a set. tags: - reference x-app: reference x-public: true x-tool: true /v1/referral: get: description: "Returns the caller's referral code, share link and the referrals they have made.\n\nThe code is a stable,\ \ deterministic function of the org, so the link in this\nresponse is the same one every time. Each row carries the\ \ referee and the status\nof that attribution.\n\nIT IS A PURE READ. It advances no referral, grants nothing and deposits\ \ nothing\n\u2014 a GET reports state, it never changes it. Qualification is the admin sweep's\njob (POST /v1/admin/referral/sweep).\ \ The one row this handler can write is the\ncaller's OWN code-directory entry (EnsureCode), which materialises a\ \ value\nderiveCode already computes deterministically from the org id so the code has an\nO(1) reverse lookup; it\ \ carries no money, no referral state and no other tenant." operationId: get_referral responses: '200': content: application/json: schema: $ref: '#/components/schemas/referral.myReferrals' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the caller's referral code, share link and the referrals they have made. tags: - referral x-app: referral x-public: true x-tool: true /v1/referral/claim: post: description: "Records that the caller's org signed up through a referral code.\n\nThe REFEREE is the validated caller,\ \ never a client field, and the referrer is\nresolved from the code \u2014 so a caller can only ever attach THEMSELVES\ \ to someone\nelse's code. Referring yourself is 400 and an unknown code is 404.\n\nIt is idempotent and first-touch:\ \ an org can be referred once, ever. A repeat\ncall returns the referral already on file with created=false and 200,\ \ where the\nfirst call answers 201.\n\nRecording a referral grants nothing, and neither does anything downstream\ \ of it:\nthe edge later advances to qualified when the referee makes metered spend\n(POST /v1/admin/referral/sweep),\ \ and that is the end of it. No credit is ever\nissued from this package." operationId: post_referral_claim requestBody: content: application/json: example: code: H4NZ0ABC schema: $ref: '#/components/schemas/referral.claimRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/referral.claimView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records that the caller's org signed up through a referral code. tags: - referral x-app: referral x-public: true x-tool: true /v1/registry/images: get: description: "Lists the org's container repositories, read live from the OCI\ncatalog and filtered server-side to the\ \ org's namespace \u2014 the page can only\never hold the caller's own images." operationId: get_registry_images responses: '200': content: application/json: schema: $ref: '#/components/schemas/registry.registryImageList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the org's container repositories, read live from the OCI catalog and filtered server-side to the org's\ \ namespace \u2014 the page can only ever hold the caller's own images." tags: - registry x-app: registry x-public: true x-tool: true /v1/registry/packages: get: description: "Lists the org's npm packages \u2014 `` and `@/\u2026` \u2014 from the\nnpm registry's search\ \ index, optionally narrowed by a query within that\nscope. The org boundary is applied server-side after the search,\ \ so a query\ncan never widen it." operationId: get_registry_packages parameters: - description: 'Query narrows the listing within the org''s scope when present; the org boundary itself is never widened by it. It rides the query string.' in: query name: query required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/registry.registryPackageList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the org's npm packages \u2014 `` and `@/\u2026` \u2014 from the npm registry's search index,\ \ optionally narrowed by a query within that scope." tags: - registry x-app: registry x-public: true x-tool: true /v1/registry/projects: get: description: "Lists the namespaces the caller can see with what each holds: the\norg's slug, its repository count on\ \ the OCI catalog, and its package count\non the npm registry. Today that is exactly one row \u2014 the caller's org." operationId: get_registry_projects responses: '200': content: application/json: schema: $ref: '#/components/schemas/registry.registryProjectList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Lists the namespaces the caller can see with what each holds: the org''s slug, its repository count on the OCI catalog, and its package count on the npm registry.' tags: - registry x-app: registry x-public: true x-tool: true /v1/registry/status: get: description: "Reports whether the OCI and npm registries are reachable and, when\nthe OCI half is auth-gated, which\ \ token realm its challenge advertises \u2014 an\nhonest lens for \"is the registry plane up\", never a fabricated\ \ ok." operationId: get_registry_status responses: '200': content: application/json: schema: $ref: '#/components/schemas/registry.registryStatus' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Reports whether the OCI and npm registries are reachable and, when the OCI half is auth-gated, which token\ \ realm its challenge advertises \u2014 an honest lens for \"is the registry plane up\", never a fabricated ok." tags: - registry x-app: registry x-public: true x-tool: true /v1/registry/tags: get: description: "Lists one org-owned repository's tags, read live from the OCI registry.\nThe repository is addressed inside\ \ the org's namespace \u2014 a name outside it\ncannot be expressed, and an unknown one answers 404." operationId: get_registry_tags parameters: - description: 'Image is the repository name inside the org''s namespace, as returned by the images op. It rides the query string.' in: query name: image required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/registry.registryTagList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists one org-owned repository's tags, read live from the OCI registry. tags: - registry x-app: registry x-public: true x-tool: true /v1/registry/token: post: description: "Mints a short-lived, pull-only registry token for exactly one of the\norg's images, through the same IAM\ \ realm the docker CLI authenticates\nagainst. The scope is pinned server-side to `/` with the `pull`\n\ action \u2014 no field exists to name another org's image or ask for push. Use it\nas `Authorization: Bearer \u2026\ ` on the OCI wire; it expires in minutes." operationId: post_registry_token requestBody: content: application/json: example: image: cloud schema: $ref: '#/components/schemas/registry.registryMint' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/registry.registryToken' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Mints a short-lived, pull-only registry token for exactly one of the org's images, through the same IAM realm the docker CLI authenticates against. tags: - registry x-app: registry x-public: true x-tool: true /v1/rerank: post: description: "Implements POST /v1/rerank (Cohere/Jina-compatible).\n\nBody: {\"model\": \"...\", \"query\": \"...\"\ , \"documents\": [\"...\", ...]|[{\"text\":\"...\"}],\n\n\t\"top_n\"?: int, \"return_documents\"?: bool}\n\nResponse:\ \ {\"object\":\"list\",\"model\":...,\"results\":[{\"index\",\"relevance_score\",\"document\"?}],\"usage\":{...}}\n\ \nBackend selection is provider-driven (one endpoint, one contract):\n - If the model routes to a native rerank provider\ \ (Jina/Cohere/Voyage) the\n request is proxied to that provider's /rerank endpoint.\n - Otherwise scores are\ \ computed as a real bi-encoder ranking: embed the\n query and documents through the resolved embedding model and\ \ rank by\n cosine similarity. No rerank-specific key required." operationId: post_rerank responses: '200': content: application/json: schema: $ref: '#/components/schemas/ai.Ranking' description: Success. '401': description: No credential, or one this service does not accept. '403': description: A valid credential that may not do this. summary: Implements POST /v1/rerank (Cohere/Jina-compatible). tags: - ai x-app: ai x-public: true /v1/responses: post: description: 'Implements POST /v1/responses. The converted request is completed by the chat path, which is handed a sink saying where the answer goes: a stream is translated as it is produced, a whole body is translated entire.' operationId: post_responses responses: '200': content: application/json: schema: $ref: '#/components/schemas/ai.ResponsesResource' description: Success. '401': description: No credential, or one this service does not accept. '403': description: A valid credential that may not do this. summary: Implements POST /v1/responses. tags: - ai x-app: ai x-public: true /v1/risk/features: get: description: "Features is the feature catalogue in its two honest lenses.\n\nThe MODEL lens is the governed inventory:\ \ one entry per dimension of the model\nspace, each carrying the typology it serves, the supervisor's own words for\ \ the\nindicator, and the published standard those words come from \u2014 so a coverage\nclaim is checkable rather\ \ than asserted. It is the same for every organisation.\n\nThe SURFACE lens is what THIS organisation's own event\ \ surface actually carries,\nmeasured over the window: how many of its buckets carry each dimension at all,\nand what\ \ the dimension reads where it is present. A dimension present in no\nbucket is BLIND, and saying so is the difference\ \ between no risk and no data." operationId: riskFeatures parameters: - description: 'Days is how far back to measure the organisation''s own coverage, 1 to 400. Zero takes thirty.' example: 30 in: query name: days required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/risk.riskCatalog' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'The feature catalogue: what the model reads, and what your surface carries' tags: - risk x-app: risk x-public: true x-tool: true /v1/risk/health: get: description: "Reports whether the per-organisation model plane is genuinely usable: that the plane was built, that the\ \ per-organisation stores can be written, and whether the event surface the feature plane is rolled up from is reachable.\ \ It is a REAL probe, not status theatre.\n\n200 only when the plane can work. Otherwise 503 CARRYING THE REPORT \u2014\ \ which part failed and the real error \u2014 and that body is why this is not a typed op: a typed op reaches a non-2xx\ \ by returning an error, and the envelope that produces would drop exactly the detail the probe exists to deliver.\n\ \nAn unreachable event surface is REPORTED and is not a failure. Scoring reads in-memory aggregates and never the\ \ warehouse, so a warm that cannot run degrades how much history a model has seen and does not stop it deciding.\n\ \nIt also reports how many organisations' models are resident, how many have been evicted to hold that bound, and\ \ how many of the resident ones are at their own aggregate bound. Eviction is lossless \u2014 learned state is written\ \ to that organisation's own store first and its aggregates rebuild from its own record \u2014 so a climbing count\ \ is a capacity signal, not a loss. A STRAINED model is different: it has started forgetting its own least-recently-active\ \ subjects, and each forgotten subject reads as inactive until it is active again. That is a control degrading, and\ \ it is reported here because it is otherwise silent.\n\nIt answers about the process, not about a tenant: it takes\ \ no organisation and names none." operationId: get_risk_health summary: Whether the risk model plane can actually work right now tags: - risk x-app: risk x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/risk/learn: post: description: "Records a batch of events into the caller organisation's own aggregates\nand lets its model learn from\ \ them. It answers how many it learned from.\n\nIT DOES NOT SCORE, AND THAT IS THE POINT. An observation is a value\ \ you record;\nlearning is a transformation over observations; a verdict is a query against the\nresult. This op is\ \ the first two. [ops.score] is the third, it is pure, and it\nis the ONE entry point to a verdict. They were one\ \ call, which meant you could\nnot record without training and could not train without being answered \u2014 and the\n\ model ran twice over every event to produce a verdict the response carried and\nno caller read.\n\nTO OBSERVE AND\ \ JUDGE, COMPOSE THE TWO, and mind the order. Score FIRST, then\nlearn: the score is then the model's opinion of an\ \ event it has not yet learned\nfrom, which is the question worth asking. The other order answers for a model\nthat\ \ has already absorbed the event it is judging.\n\nThis is the training path, and there is no job behind it: the model\ \ IS a set of\nmass counters over half-space trees, so learning is an increment and the model\nis current the instant\ \ the last event lands. Nothing from any other\norganisation is in it, and nothing from this organisation leaves it.\n\ \nA RETRY IS INERT. The record deduplicates on the event id you send, and an event\nalready in it moves nothing, costs\ \ nothing and is not counted \u2014 so a client that\ntimed out can send the same batch again and its model holds\ \ what it holds.\nWithout an id of your own there is nothing to converge on: two identical bodies\nare two events." operationId: riskLearn requestBody: content: application/json: example: events: - id: tx_9 kind: account nano: 420000000 subject: u_412 schema: $ref: '#/components/schemas/risk.riskLearnIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/risk.riskLearnOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Teach your organisation's own model from its own events tags: - risk x-app: risk x-public: true x-tool: true /v1/risk/policy: get: description: "Reports the caller organisation's own decision-regime history: every\ndistinct regime it has adopted,\ \ which version is in force, and what retention\nhas taken.\n\nWHY IT EXISTS. Every score cites the version it was\ \ decided under\n([riskScoreOut.Policy]), and the threshold that score was measured against is\nderived from the appetite\ \ that version states. Restate the appetite and, without\nthis record, every earlier decision becomes unreconstructible\ \ \u2014 the cut it was\njudged by no longer exists anywhere. An adverse decision that cannot be\nexplained against\ \ the policy in force when it was taken cannot be defended.\n\nIt covers ONE organisation. The history is on that\ \ organisation's own shelf, so\nanother's versions are not filtered out of the answer \u2014 they are not in the file\n\ the answer is read from." operationId: riskPolicy responses: '200': content: application/json: schema: $ref: '#/components/schemas/risk.riskPolicyOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Your organisation's decision-regime history, and which version is in force tags: - risk x-app: risk x-public: true x-tool: true put: description: "States the decision regime the caller organisation's model decides\nunder: how much of its own stream\ \ may be sent for examination, how much of the\nrest is sampled to measure what was missed, and whether the model\ \ may change an\noutcome at all.\n\nThe appetite is the decision a model is not permitted to make for itself: its\n\ output is a probability, so how likely it is to MISS something is a matter of\npolicy that has to be stated, measured\ \ and reviewed rather than absorbed into a\nconstant. The alert threshold is derived from it as a quantile of the\ \ scores\nactually observed, which is what keeps its meaning as the distribution drifts.\n\nIt is DURABLE BEFORE IT\ \ IS IN FORCE. The regime is recorded as a new version on\nthe organisation's own shelf before anything in memory\ \ moves, so a policy that\ncannot be written down is refused rather than answered from state the next\nrollout would\ \ silently undo.\n\nARMING IS AN ADMIN ACT AND TUNING IS NOT. Setting `live` requires an admin of\nthis organisation;\ \ stating the appetite and the sample is self-service for any\nmember. Taking the model live decides whether it may\ \ change an OUTCOME at all \u2014\na payment frozen, a grant refused \u2014 for every customer this organisation has,\n\ and that is a decision an organisation takes rather than one of its members.\n\nA RESTATEMENT OF THE REGIME IN FORCE\ \ MINTS NOTHING and answers the version\nalready in force. Compare the version you receive with the version you had:\n\ unchanged means the numbers were the same, which is why there is no flag for it.\n\nLearned state survives the change.\ \ The model's identity covers its SHAPE \u2014 the\ninventory and the geometry \u2014 and not its appetite, so restating\ \ policy unlearns\nnothing. It also does not REPORT the learned state: what the model is is read\nfrom the model." operationId: riskSetPolicy requestBody: content: application/json: example: live: false review: 0.01 sample: 0.001 schema: $ref: '#/components/schemas/risk.riskAppetiteIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/risk.riskPolicyOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'State the decision regime: the appetite, the sample, and whether the model is live' tags: - risk x-app: risk x-public: true x-tool: true /v1/risk/score: post: description: 'Judges one event against the caller organisation''s OWN model and learns nothing from it. It is how a candidate is tried against real behaviour before anything depends on the answer, and it is the model''s analogue of testing a rule. Because it records nothing, the aggregates it reads do not include the event: the numbers are the organisation''s history as it stands. A model still warming declines with a reason rather than answering zero, because silence must never read as a clean result.' operationId: riskScore requestBody: content: application/json: example: event: id: tx_9 kind: account nano: 420000000 subject: u_412 schema: $ref: '#/components/schemas/risk.riskScoreIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/risk.riskScoreOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Score one event against your organisation's own model tags: - risk x-app: risk x-public: true x-tool: true /v1/risk/search: post: description: "Runs an exhaustive search for the model shape that best fits the caller\norganisation's own history, and\ \ answers 202 with the run to read back.\n\nEvery candidate is replayed over that organisation's OWN feature surface\ \ in its\nown sandbox \u2014 its own aggregates, its own model, neither of them the live one \u2014\nso a run cannot\ \ move a live threshold and cannot see another organisation's\ndata. The result is the learning curve for each shape\ \ and the one that fit\nbest, ranked on how closely it honoured the stated appetite, whether it warmed\nat all, whether\ \ it saturated, and how much of the coordinate space it left\nblind.\n\nAn empty history is REFUSED rather than reported\ \ as zero alerts, because \"no\nalerts\" is exactly what a quiet model looks like." operationId: riskSearch requestBody: content: application/json: example: days: 30 schema: $ref: '#/components/schemas/risk.riskSearchIn' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/risk.riskSearchRun' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Search exhaustively for the model shape that fits your own history tags: - risk x-app: risk x-public: true x-tool: true /v1/risk/search/{id}: get: description: "Reads back one search run: every shape tried over this\norganisation's own history, best first, and the\ \ one that fit.\n\nA run another organisation started is simply not there \u2014 the same 404 an\nunknown id gives,\ \ so the read is not a probe oracle." operationId: riskSearchResult parameters: - description: "ID is the run, taken from the path. A run another organisation started is\nsimply not there \u2014 the\ \ same answer an unknown id gives." example: srch_2f6a1c in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/risk.riskSearchReport' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Read back one exhaustive search tags: - risk x-app: risk x-public: true x-tool: true /v1/risk/state: get: description: 'Reports the caller organisation''s own model: what it has learned, whether it is live or still in shadow, the threshold in force, the appetite it stated beside the share it actually realised, every refusal by reason, every feature that read blind, and how much of the organisation''s own event surface has been folded in. It covers ONE organisation. A caller cannot learn another''s volumes, alert rate or behaviour from it, because the state is read out of a model that holds only its own.' operationId: riskState responses: '200': content: application/json: schema: $ref: '#/components/schemas/risk.riskModelState' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Report your organisation''s model: what it learned, and what it realised' tags: - risk x-app: risk x-public: true x-tool: true /v1/risk/state/model: post: description: "Publishes your organisation's model as a NAMED VALUE, so a decision\ntaken today can be reconstructed\ \ tomorrow and a change made today can be undone.\n\nIt answers with a NAME and not with the state. The masses stay\ \ on your\norganisation's own encrypted store and are referred to by an address computed\nfrom their own content:\ \ the shape, the geometry seed, the position in the window,\nthe threshold, the masses themselves as IEEE-754 bits,\ \ and the fold watermark\nbehind them. That is what makes the value nameable without making the caller its\ncustodian.\n\ \nIT IS IDEMPOTENT ON THE VALUE. A model that has not changed publishes to the name\nit already has and mints nothing,\ \ reporting minted=false \u2014 so publishing at every\nboundary that matters is free. Ten values are retained per\ \ organisation, bounded\nin BYTES rather than in rows, and the oldest is disposed of past that.\n\nA model that has\ \ learned nothing is refused: planted is not learned, and a value\nthat reproduces nothing is not a value.\n\nIt is\ \ POST and PUT on one address because they are one plane's two verbs over one\nkind of thing: POST mints a value from\ \ the model in force, PUT puts a value in\nforce. They were /v1/risk/state/snapshot and /v1/risk/state/restore \u2014\ \ two addresses\nnamed after the operation rather than after the thing, which is how a reader ends\nup asking what\ \ the difference between a snapshot and a value is." operationId: riskPublishModel responses: '201': content: application/json: schema: $ref: '#/components/schemas/risk.riskPublishOut' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Publish your organisation's model as a named, immutable value tags: - risk x-app: risk x-public: true x-tool: true put: description: "Puts one of your organisation's OWN PUBLISHED VALUES in force, by name \u2014\nwhich is what an instant\ \ rollback is, what promoting a challenger is, and what\ninstalling the shape a search found is.\n\nIT TAKES AN ADDRESS\ \ AND NEVER STATE. The masses are read from your own store, so\nnothing about your model has to be held by whatever\ \ is making this call. That\ncloses the sharpest edge the previous shape had: a body of counters is something\na caller\ \ can COMPOSE, and a region filled until activity inside it reads as\nordinary is a model that has been shaped rather\ \ than learned. The engine's mass\ninvariant was the only thing standing between a composed body and the model; with\n\ an address there is no body to compose.\n\nIT ADOPTS THE SHAPE, NOT ONLY THE MASSES. A value records the model space\ \ its\nmasses were taken in, and a value whose space differs from the one in force\nREPLANTS your model into that\ \ space before restoring them. That is what makes\nPOST /v1/risk/search actionable: a search answers with the shape\ \ that fits your own\nhistory best and publishes it fitted, and its address is what you name here. Before\nthis, a\ \ winning shape was advice nobody could take \u2014 the adoption path refused every\nshape change, and a winner is\ \ a different shape by definition.\n\nWHAT ADOPTING A SEARCHED SHAPE COSTS, SAID PLAINLY: the value a search fits\ \ has\nlearned the window the search replayed and nothing older, so installing it trades\nhistory for fit. Your appetite\ \ is untouched \u2014 that is your policy record's, with its\nown versions \u2014 and so is the geometry, which stays\ \ your own.\n\nAn address your organisation has not published is NOT FOUND. That includes one\nanother organisation\ \ published, and it is not a lookup that failed: the store is\nper organisation and the address is a name, never an\ \ authority." operationId: riskAdoptModel requestBody: content: application/json: schema: $ref: '#/components/schemas/risk.riskAdoptIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/risk.riskModelState' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Put one of your organisation's own published model values in force tags: - risk x-app: risk x-public: true x-tool: true /v1/runner/declare: post: description: 'Republishes what a registered runner can do, and answers with what this side understands, so the two learn about each other from one exchange.' operationId: post_runner_declare parameters: - in: header name: x-runner-uuid required: false schema: type: string - in: header name: x-runner-token required: false schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/runner.DeclareIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/runner.DeclareOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Republishes what a registered runner can do, and answers with what this side understands, so the two learn about each other from one exchange. tags: - git x-app: git x-public: true x-tool: true /v1/runner/log: post: description: 'Adds console output to a task''s log and answers with how far that log is durable, so the runner knows where to resend from.' operationId: post_runner_log parameters: - in: header name: x-runner-uuid required: false schema: type: string - in: header name: x-runner-token required: false schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/runner.LogIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/runner.LogOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Adds console output to a task's log and answers with how far that log is durable, so the runner knows where to resend from. tags: - git x-app: git x-public: true x-tool: true /v1/runner/register: post: description: 'Trades a pool''s join secret for a runner identity and the token that authenticates every later call. It is the one operation with no credential to check, because a runner has none until this answers. The secret names the pool it opens, and a pool exists only because somebody declared it. A daemon that starts against capacity nobody declared is refused here, which is where the rule that pools are declared state actually holds.' operationId: post_runner_register requestBody: content: application/json: schema: $ref: '#/components/schemas/runner.RegisterIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/runner.RegisterOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Trades a pool's join secret for a runner identity and the token that authenticates every later call. tags: - git x-app: git x-public: true x-tool: true /v1/runner/state: post: description: "Records a task's progress and that of its steps, and answers with the\nresult this side now holds \u2014\ \ which is how a runner learns its task was stopped\nfrom somewhere else." operationId: post_runner_state parameters: - in: header name: x-runner-uuid required: false schema: type: string - in: header name: x-runner-token required: false schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/runner.StateIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/runner.StateOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Records a task's progress and that of its steps, and answers with the result this side now holds \u2014 which\ \ is how a runner learns its task was stopped from somewhere else." tags: - git x-app: git x-public: true x-tool: true /v1/runner/task: post: description: 'Hands the runner a job to execute, if its pool has one, and answers immediately either way. A runner sends the queue version it last saw; when it matches, nothing has been queued since and no lease transaction is opened.' operationId: post_runner_task parameters: - in: header name: x-runner-uuid required: false schema: type: string - in: header name: x-runner-token required: false schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/runner.TaskIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/runner.TaskOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Hands the runner a job to execute, if its pool has one, and answers immediately either way. tags: - git x-app: git x-public: true x-tool: true /v1/s3/buckets: get: description: 'Lists the caller org''s own buckets. Only the caller''s: every bucket is physically named under a per-org prefix and the listing strips that prefix, so a tenant sees friendly names and another tenant''s buckets are not in the answer at all. Another org''s bucket is not refused but INVISIBLE, so this cannot be used to learn that a name is taken elsewhere. Billed per call: the balance is checked BEFORE anything is touched, so an unfunded org is refused with nothing done, and the debit lands only once the work has succeeded.' operationId: get_s3_buckets responses: '200': content: application/json: schema: $ref: '#/components/schemas/s3.bucketList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller org's own buckets. tags: - s3 x-app: s3 x-public: true x-tool: true post: description: 'Makes a new bucket for the caller''s org and answers 201 with it. The physical name is derived from the caller''s validated org, so a tenant can only ever create inside its own namespace and no request field can redirect that. A name already taken in the org is 409. Billed per call: the balance is checked BEFORE anything is touched, so an unfunded org is refused with nothing created, and the debit lands only once the bucket exists.' operationId: post_s3_buckets requestBody: content: application/json: schema: $ref: '#/components/schemas/s3.bucketIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/s3.bucketItem' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Makes a new bucket for the caller's org and answers 201 with it. tags: - s3 x-app: s3 x-public: true x-tool: true /v1/s3/buckets/{bucket}: delete: description: 'Removes an EMPTY bucket and answers 204. A non-empty bucket is 409 rather than a cascade: deleting a tenant''s objects behind a single bucket call is not a thing this surface will do silently. A bucket the caller''s org does not own is the same 404 an unknown name gives. Billed per call: the balance is checked BEFORE anything is touched, so an unfunded org is refused with nothing deleted, and the debit lands only once the bucket is gone.' operationId: delete_s3_buckets_by_bucket parameters: - description: Bucket is the bucket's friendly name, from the path. in: path name: bucket required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes an EMPTY bucket and answers 204. tags: - s3 x-app: s3 x-public: true x-tool: true /v1/s3/buckets/{bucket}/objects: get: description: "Lists one folder level of a bucket.\n\nFolder-style by default: sub-prefixes come back as directory entries,\ \ which is\nthe file-manager view. `?recursive=true` lists every key flat under the prefix\ninstead. Keys are RELATIVE\ \ to `?prefix=`, and the listing is bounded so a huge\nbucket cannot exhaust memory \u2014 Total is what came back,\ \ not what the bucket\nholds.\n\nBilled per call: the balance is checked BEFORE anything is touched, so an\nunfunded\ \ org is refused with nothing read, and the debit lands only once the\nlisting has succeeded." operationId: get_s3_buckets_by_bucket_objects parameters: - description: Bucket is the bucket to list, from the path. in: path name: bucket required: true schema: type: string - in: query name: prefix required: false schema: type: string - in: query name: recursive required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/s3.objectList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists one folder level of a bucket. tags: - s3 x-app: s3 x-public: true x-tool: true post: description: "Mints a presigned PUT URL the caller uploads to DIRECTLY.\n\nThe bytes never pass through this binary\ \ and the admin credential never leaves\nthe server: the URL is signed against the PUBLIC host, scoped to exactly\ \ this\nbucket and key, and expires. A deployment with no public endpoint configured\ncannot mint one and answers\ \ 503 rather than a URL that will not work.\n\nBilled per call \u2014 for MINTING the URL, which is the work this\ \ operation does;\nthe upload that follows it goes straight to the store and is not seen here. The\nbalance is checked\ \ BEFORE anything is touched, so an unfunded org is refused\nwith no URL issued." operationId: post_s3_buckets_by_bucket_objects parameters: - description: Bucket is the bucket to upload into, from the path. in: path name: bucket required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/s3.uploadIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/s3.presignResponse' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Mints a presigned PUT URL the caller uploads to DIRECTLY. tags: - s3 x-app: s3 x-public: true x-tool: true /v1/s3/health: get: description: "Reports whether this deployment can serve object storage.\n\nIt is a REAL probe rather than a constant:\ \ 200 when admin credentials are\npresent, so the store is reachable in principle, and 503 with the reason when\n\ they are not. It is deliberately NOT gated \u2014 liveness has to be probe-able\nwithout a token \u2014 so it is the\ \ one operation here that names no bucket and\nbills nothing." operationId: get_s3_health responses: '200': content: application/json: schema: $ref: '#/components/schemas/s3.s3Health' description: ok '503': content: application/json: schema: $ref: '#/components/schemas/s3.s3Health' description: service unavailable default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports whether this deployment can serve object storage. tags: - s3 x-app: s3 x-public: true x-tool: true /v1/sandbox: get: description: 'Lists the sandboxes the caller holds, newest first. A member holds the sandboxes they leased; an admin of the org, or a SuperAdmin, holds every one in it. `?project=` and `?status=` narrow it. Only the caller''s org''s: the store is keyed on the validated org, so another tenant''s sandbox is not something this operation can return.' operationId: get_sandbox parameters: - in: query name: project required: false schema: type: string - in: query name: status required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/sandbox.sandboxList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the sandboxes the caller holds, newest first. tags: - sandbox x-app: sandbox x-public: true x-tool: true post: description: "Leases a sandbox \u2014 a real computer \u2014 for the caller's org.\n\nThe class decides what it is for\ \ and therefore its image, working directory\nand isolation. A dev or desktop sandbox is SINGLE-ATTACH per project,\ \ so\nasking twice for one project resumes the one that exists rather than paying\nfor a second; an exec sandbox carries\ \ no project and is bounded per org\ninstead, refused 429 past the ceiling because the caller's correct response is\n\ to wait.\n\nAnswers 201 with the sandbox as leased, which names the runtime it GOT \u2014 not\nthe one that was asked\ \ for." operationId: post_sandbox requestBody: content: application/json: schema: $ref: '#/components/schemas/sandbox.sandboxIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/sandbox.Sandbox' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Leases a sandbox \u2014 a real computer \u2014 for the caller's org." tags: - sandbox x-app: sandbox x-public: true x-tool: true /v1/sandbox/end: post: description: 'Ends the caller''s sandbox lease: the pod goes, and the volume goes only when the caller asked for that too.' operationId: end_sandbox requestBody: content: application/json: schema: $ref: '#/components/schemas/sandbox.EndIn' required: true responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: End a sandbox and release it tags: - sandbox x-app: sandbox x-public: true x-tool: true /v1/sandbox/lease: post: description: "Leases the caller's sandbox, or returns the one it named if that\nlease is still running, or gives it\ \ a pod again if it is parked \u2014 with its\ndisk as it was left.\n\nWhat comes back is a real computer: a pod under\ \ a runtime boundary with a\ntoolchain already in it, its own filesystem, and a lease that ends it. Every\nother op\ \ here acts on the one this returns." operationId: lease_sandbox requestBody: content: application/json: schema: $ref: '#/components/schemas/sandbox.LeaseIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/sandbox.Leased' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lease a sandbox \u2014 a real computer \u2014 or resume one you hold" tags: - sandbox x-app: sandbox x-public: true x-tool: true /v1/sandbox/read: post: description: 'Reads one path in the caller''s sandbox: a file''s bytes, or a directory''s entries when the path names one.' operationId: read_sandbox_file requestBody: content: application/json: schema: $ref: '#/components/schemas/sandbox.PathIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/sandbox.Blob' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Read a file from a sandbox you hold tags: - sandbox x-app: sandbox x-public: true x-tool: true /v1/sandbox/run: post: description: "Runs one command inside the caller's sandbox and answers its exit code,\nstdout and stderr. A non-zero\ \ exit is a successful call carrying a failed\nprogram, so it comes back as data and not as an error.\n\nName a `session`\ \ and the command NARRATES INTO IT: its output is appended to\nthat session's live log as the program produces it,\ \ so anything watching the\nsession \u2014 GET /v1/agent/sessions/stream, scoped to one run with ?root= \u2014\nwatches\ \ the work happen rather than waiting for the verdict. Without it the\ncall is what it always was: silent until it\ \ returns, which for an agentic run\nis twenty-five minutes of blank screen.\n\nThe session is named; the TENANT is\ \ not. It is the org the caller already\nproved, so a session belonging to somebody else is absent from the org this\n\ call acts for and the append is refused there." operationId: run_in_sandbox requestBody: content: application/json: schema: $ref: '#/components/schemas/sandbox.RunIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/sandbox.Ran' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Run a command in a sandbox you hold and read its output tags: - sandbox x-app: sandbox x-public: true x-tool: true /v1/sandbox/stop: post: description: "Interrupts whatever the caller's sandbox is running and answers how\nmany commands it ended. The sandbox\ \ stays leased \u2014 stop ends the WORK, end ends\nthe RESOURCE \u2014 so whoever stopped a run can still read what\ \ it left behind." operationId: stop_run requestBody: content: application/json: schema: $ref: '#/components/schemas/sandbox.StopIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/sandbox.Stopped' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Stop what a sandbox is running, and keep the sandbox tags: - sandbox x-app: sandbox x-public: true x-tool: true /v1/sandbox/write: post: description: 'Writes bytes to one path in the caller''s sandbox, creating parents, and answers the resolved path.' operationId: write_sandbox_file requestBody: content: application/json: schema: $ref: '#/components/schemas/sandbox.WriteIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/sandbox.Wrote' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Write a file into a sandbox you hold tags: - sandbox x-app: sandbox x-public: true x-tool: true /v1/sandbox/{id}: delete: description: "Ends a sandbox and releases the compute behind it. Answers 204.\n\nENDING IS NOT STOPPING. This releases\ \ the resource: the pod goes and anything\nonly inside it goes with it. To end what a sandbox is RUNNING while keeping\n\ the sandbox \u2014 the checkout, the logs, the half-written file \u2014 the verb is\nPOST /v1/sandbox/stop.\n\n`?purge=1`\ \ additionally removes the record, so the sandbox stops being listed\nat all rather than being listed as ended." operationId: delete_sandbox_by_id parameters: - description: ID is the sandbox to end, from the path. in: path name: id required: true schema: type: string - in: query name: purge required: false schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Ends a sandbox and releases the compute behind it. tags: - sandbox x-app: sandbox x-public: true x-tool: true get: description: "Returns one sandbox: its class, project, image, the runtime it was\ngiven, its status and when its lease\ \ ends.\n\nA sandbox is its lessee's and its org admins'. An id the caller does not hold \u2014\nanother org's, or\ \ another member's \u2014 is the same 404 an unknown id gives." operationId: get_sandbox_by_id parameters: - description: ID is the sandbox to address, from the path. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/sandbox.Sandbox' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one sandbox: its class, project, image, the runtime it was given, its status and when its lease ends.' tags: - sandbox x-app: sandbox x-public: true x-tool: true /v1/sandbox/{id}/exec: post: description: "Runs one command in a sandbox the caller holds and answers with\nits exit code, stdout and stderr.\n\n\ Send `argv` \u2014 an argument vector cannot be word-split by accident \u2014 or\n`command` for a shell line, which\ \ is the only input here that ever reaches a\nshell. A non-zero exit is a SUCCESSFUL call carrying a failed command:\ \ the\nstatus is 200 and the exit code is in the answer, because \"the command failed\"\nand \"the call failed\" are\ \ different facts." operationId: post_sandbox_by_id_exec parameters: - description: ID is the sandbox to run in, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/sandbox.execRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/sandbox.ExecResult' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Runs one command in a sandbox the caller holds and answers with its exit code, stdout and stderr. tags: - sandbox x-app: sandbox x-public: true x-tool: true /v1/sandbox/{id}/fs: get: description: Reads one file from the sandbox's project directory as text, or lists the entries when the path names a directory. Paths resolve under the project root and a path that climbs out is refused rather than rewritten. operationId: get_sandbox_by_id_fs parameters: - in: path name: id required: true schema: type: string summary: Read a file, or list a directory tags: - sandbox x-app: sandbox x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). post: description: Writes the request body to one file in the sandbox's project directory, creating parent directories. Same confinement as the read above. operationId: post_sandbox_by_id_fs parameters: - in: path name: id required: true schema: type: string summary: Write a file tags: - sandbox x-app: sandbox x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/sandbox/{id}/pause: post: description: "Stops the pod, keeps the row and the volume, and settles the tail: POST\n/v1/sandbox/:id/pause.\n\nThe\ \ claim, the stop and the ship are ONE settlement for the same reason a\nretirement is \u2014 the snapshot that carries\ \ the advanced watermark has to carry\nthe new state, or a successor hydrates a running row for a pod that is gone\n\ and bills for it until the reaper notices." operationId: post_sandbox_by_id_pause parameters: - description: ID is the sandbox to address, from the path. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/sandbox.Sandbox' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Stops the pod, keeps the row and the volume, and settles the tail: POST /v1/sandbox/:id/pause.' tags: - sandbox x-app: sandbox x-public: true x-tool: true /v1/sandbox/{id}/resume: post: description: "Gives a parked sandbox a pod again: POST /v1/sandbox/:id/resume.\n\nThe pod is NEW and so is its lease:\ \ a fresh pod name, because a pod name is\nnever reused (store.go), the volume the row already names, a lease of the\n\ class's own length from now, and the RESUMING caller's credential rather than\nthe one that took the lease \u2014\ \ a session is short-lived and the one that parked\nit is gone." operationId: post_sandbox_by_id_resume parameters: - description: ID is the sandbox to address, from the path. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/sandbox.Sandbox' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Gives a parked sandbox a pod again: POST /v1/sandbox/:id/resume.' tags: - sandbox x-app: sandbox x-public: true x-tool: true /v1/sandbox/{id}/screen: get: description: "A complete, self-contained desktop \u2014 noVNC inline, no other origin \u2014 that opens its own socket\ \ and draws this sandbox's display. Embed it in an iframe and there is nothing else to build.\n\n`ticket` is the credential\ \ from the POST above, carried through to the socket. The page is NOT gated: it is inert markup and does not redeem\ \ the ticket, because a ticket is spent once and a page that spent it would hold a credential that no longer opens\ \ anything. `frame-ancestors` admits our own brands' hosts and nothing further.\n\nIt is served for every class, not\ \ only for `desktop`. The class is a fact about the image, and a sandbox with no VNC server already fails exactly\ \ \u2014 the connection is refused and the page says so \u2014 where a check here would be a second opinion about\ \ what is running inside a pod, formed from a label rather than from the pod." operationId: get_sandbox_by_id_screen parameters: - in: path name: id required: true schema: type: string summary: The screen, as a page tags: - sandbox x-app: sandbox x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/sandbox/{id}/screen/ticket: post: description: 'Mints a short-lived grant to open the screen of a desktop sandbox. Same properties as the terminal ticket, for the other endpoint.' operationId: post_sandbox_by_id_screen_ticket parameters: - description: ID is the sandbox to address, from the path. in: path name: id required: true schema: type: string responses: '201': content: application/json: schema: $ref: '#/components/schemas/sandbox.ticketGrant' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Mints a short-lived grant to open the screen of a desktop sandbox. tags: - sandbox x-app: sandbox x-public: true x-tool: true /v1/sandbox/{id}/screen/ws: get: description: "Upgrades to a WebSocket carrying RFB \u2014 the VNC wire protocol \u2014 from the sandbox's display, for\ \ a host that brings its own client. Requires `ticket`; a missing, expired or already-spent one answers 401 without\ \ upgrading.\n\nTHE WIRE IS RFB, in BINARY frames both ways, and it is not interpreted here: this is a pipe between\ \ the caller's client and the server inside the pod.\n\nTHE PIXELS COME OUT THROUGH THE EXEC CHANNEL. The display\ \ binds 127.0.0.1 only and deliberately nothing else, so there is no address to dial \u2014 `socat` joins the stream\ \ to that loopback port over the same Kubernetes exec subresource every other call into a sandbox uses. One way in,\ \ one thing to authorize, nothing further exposed.\n\nThe window size is ignored. A browser pane is not the X server's\ \ geometry, and the client scales what it is given rather than asking a server with no RandR to resize itself." operationId: get_sandbox_by_id_screen_ws parameters: - in: path name: id required: true schema: type: string summary: The screen, as a socket tags: - sandbox x-app: sandbox x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/sandbox/{id}/terminal: get: description: "A complete, self-contained terminal \u2014 xterm inline, no other origin \u2014 that opens its own socket\ \ and runs a shell in this sandbox. Embed it in an iframe and there is nothing else to build.\n\n`ticket` is the credential\ \ from the POST above and `arg` names the session (see the socket below); both are simply carried through to the socket.\ \ The page is NOT gated \u2014 it is inert markup and does not redeem the ticket, because a ticket is spent once and\ \ a page that spent it would hold a credential that no longer opens anything.\n\nWhen the terminal is up it posts\ \ `{source:\"hanzo-term\", ready:true}` to its parent frame, so a host can tell a live terminal from a page that failed\ \ into something else. `frame-ancestors` admits our own brands' hosts and nothing further." operationId: get_sandbox_by_id_terminal parameters: - in: path name: id required: true schema: type: string summary: The terminal, as a page tags: - sandbox x-app: sandbox x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/sandbox/{id}/terminal/ticket: post: description: 'Mints a short-lived grant to open a terminal on a sandbox. The ticket travels in the query string of the URL it answers with, because a browser cannot set an Authorization header on a WebSocket handshake. It is single-purpose and short-lived for exactly that reason. A sandbox that is not running is 409 rather than a ticket that cannot be used.' operationId: post_sandbox_by_id_terminal_ticket parameters: - description: ID is the sandbox to address, from the path. in: path name: id required: true schema: type: string responses: '201': content: application/json: schema: $ref: '#/components/schemas/sandbox.ticketGrant' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Mints a short-lived grant to open a terminal on a sandbox. tags: - sandbox x-app: sandbox x-public: true x-tool: true /v1/sandbox/{id}/terminal/ws: get: description: "Upgrades to a WebSocket carrying a login shell on a pseudo-terminal inside the sandbox \u2014 for a host\ \ that brings its own emulator. Requires `ticket`; a missing, expired or already-spent one answers 401 without upgrading.\n\ \nTHE WIRE. A text frame is stdin, unless it is the one control object `{\"resize\":{\"cols\":N,\"rows\":M}}`; a binary\ \ frame is always stdin. Output comes back as BINARY frames, because a pty emits arbitrary bytes cut at arbitrary\ \ offsets and a text frame carrying half a rune is one the browser closes the connection over.\n\n`arg` names a SESSION:\ \ the shell runs under `tmux new -A -s `, which attaches to that session if it exists and creates it if it does\ \ not \u2014 so one sandbox holds as many terminals as a caller has names for. It is 1-64 characters of letters, digits,\ \ `-` or `_` and may not begin with `-`; anything else is 400. Without `arg` the shell is unnamed and unmultiplexed.\n\ \nThe shell is `zsh -l`, falling back to `bash -l` and then to `sh -l`, and to the plain shell again when the image\ \ has no tmux. Every step is a preference and none is a requirement: whatever else the image carries \u2014 the hanzo\ \ CLI included \u2014 is a command to type, never a condition for getting a prompt." operationId: get_sandbox_by_id_terminal_ws parameters: - in: path name: id required: true schema: type: string summary: The terminal, as a socket tags: - sandbox x-app: sandbox x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/sbom: post: description: "Persists a CycloneDX SBOM's components keyed by image digest. Gated to a\nvalidated SuperAdmin (owner\ \ == AdminOrg) \u2014 the canonical cloud super-admin\ncheck, which the build fleet / CI carries. Re-ingest is idempotent:\ \ rows share\nthe (digest, name, version, purl) ORDER BY, so ReplacingMergeTree keeps the\nlatest by ingested_at (and\ \ resolve reads FINAL)." operationId: post_sbom requestBody: content: application/json: example: document: components: [] format: cyclonedx imageDigest: sha256:abc imageRef: oci.hanzo.ai/hanzo/cloud:v1 schema: $ref: '#/components/schemas/sbom.SbomIngest' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/sbom.SbomIngested' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Persists a CycloneDX SBOM's components keyed by image digest. tags: - sbom x-app: sbom x-public: true x-tool: true /v1/sbom/health: get: description: 'Health is a pure liveness probe: the service is up; datastore reflects whether the datastore store is connected. Not JWT-gated, always 200 (a disconnected datastore is degraded-but-alive; the data endpoints report that as 503).' operationId: get_sbom_health responses: '200': content: application/json: schema: $ref: '#/components/schemas/sbom.SbomHealth' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Health is a pure liveness probe: the service is up; datastore reflects whether the datastore store is connected.' tags: - sbom x-app: sbom x-public: true x-tool: true /v1/search: post: description: 'Is the typed op behind POST /v1/search. It does exactly two things the in-process entry point must not do: resolve the tenant from the validated principal, and refuse when there is none. Everything else is ForOrg.' operationId: search requestBody: content: application/json: schema: $ref: '#/components/schemas/search.Request' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/search.Fusion' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Hybrid search over the org's own corpora tags: - search x-app: search x-public: true x-tool: true /v1/security/findings: get: description: "Is the org's findings \u2014 rule, severity, path, line, masked preview\nand fingerprint \u2014 newest\ \ first, across scans or within one.\n\nA minSeverity outside critical|high|medium|low is refused rather than quietly\n\ ignored, so a filter typo cannot read as \"no findings\". Strictly org-scoped, and\na caller with no validated org\ \ is refused." operationId: get_security_findings parameters: - description: ScanID narrows to a single scan. in: query name: scanId required: false schema: type: string - description: 'MinSeverity drops everything below that rank: critical, high, medium or low. A value outside that set is refused rather than quietly ignored, so a filter typo cannot read as "no findings".' in: query name: minSeverity required: false schema: type: string - description: Limit caps the page. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/security.findingList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Is the org's findings \u2014 rule, severity, path, line, masked preview and fingerprint \u2014 newest first,\ \ across scans or within one." tags: - security x-app: security x-public: true x-tool: true /v1/security/findings/{id}: get: description: "Returns a single finding: which rule fired, where (path and line),\nthe masked preview and the SHA-256\ \ fingerprint of the secret \u2014 the raw secret is\nnot stored and cannot be read back.\n\nScoped to the caller's\ \ org, and a finding belonging to another org is the same\n404 as one that never existed." operationId: get_security_findings_by_id parameters: - description: ID is the finding the URL names. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/security.findingView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns a single finding: which rule fired, where (path and line), the masked preview and the SHA-256 fingerprint\ \ of the secret \u2014 the raw secret is not stored and cannot be read back." tags: - security x-app: security x-public: true x-tool: true /v1/security/health: get: description: "Reports that the scanning subsystem is serving and how many\nsecret-detection rules the engine holds.\n\ \nIt has no external dependency \u2014 the answer is ok whenever the findings store\nopened \u2014 so it measures\ \ this process rather than anything downstream. It reads\nno tenant: a prober that sends no principal is answered,\ \ not refused." operationId: get_security_health responses: '200': content: application/json: schema: $ref: '#/components/schemas/security.ruleset' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports that the scanning subsystem is serving and how many secret-detection rules the engine holds. tags: - security x-app: security x-public: true x-tool: true /v1/security/rules: get: description: "Is the secret-detection catalog the engine scans with.\n\nIt returns every rule a scan can fire \u2014\ \ the id, name and severity a finding\ncites \u2014 so a caller can render or triage results without hard-coding the\n\ catalog. It is the same for everyone and discloses nothing tenant-specific, so\nit carries no org scope." operationId: get_security_rules responses: '200': content: application/json: schema: $ref: '#/components/schemas/security.ruleList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Is the secret-detection catalog the engine scans with. tags: - security x-app: security x-public: true x-tool: true /v1/security/scans: get: description: "Is the org's scan history, newest first, each as the same summary the\nsubmission answered \u2014 files\ \ read, findings fired, tally by severity.\n\nStrictly org-scoped: a caller only ever sees its own scans, and one\ \ with no\nvalidated org is refused." operationId: get_security_scans parameters: - description: Limit caps the page. in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/security.scanList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Is the org's scan history, newest first, each as the same summary the submission answered \u2014 files read,\ \ findings fired, tally by severity." tags: - security x-app: security x-public: true x-tool: true post: description: "Runs the detection engine over a batch of files and answers 201 with\nthe scan summary: how many files\ \ were read, how many findings fired, and the\ntally by severity.\n\nTHE SUBMITTED CONTENT IS NEVER STORED. It is\ \ scanned in memory; what persists is\nthe finding \u2014 its rule, its path and line, a MASKED preview (first and\ \ last\ncharacters kept, the middle starred) and the SHA-256 fingerprint of the raw\nsecret. The fingerprint is what\ \ makes the same secret recognisable across scans\nand after rotation without the secret ever being written down.\n\ \nIt requires a validated org, which scopes the stored scan and every finding on\nit; a caller with no org is refused.\ \ Bounded at 500 files and 8 MiB of total\ncontent per submission \u2014 split a larger tree across scans. One scan\ \ is one\nmetered unit, and the scan is recorded in the audit log with its tally, never\nwith its findings." operationId: post_security_scans requestBody: content: application/json: schema: $ref: '#/components/schemas/security.submitReq' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/security.scanView' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Runs the detection engine over a batch of files and answers 201 with the scan summary: how many files were read, how many findings fired, and the tally by severity.' tags: - security x-app: security x-public: true x-tool: true /v1/security/scans/{id}: get: description: 'Returns one scan together with every finding on it, so the detail view is one round-trip rather than a list call per scan. The findings carry masked previews and fingerprints, never secrets. Scoped to the caller''s org: a scan id belonging to another org is the same 404 as an id that never existed, so a ruleset learns nothing about what exists elsewhere. No validated org is refused.' operationId: get_security_scans_by_id parameters: - description: ID is the scan the URL names. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/security.scanDetail' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one scan together with every finding on it, so the detail view is one round-trip rather than a list call per scan. tags: - security x-app: security x-public: true x-tool: true /v1/seo/audit: post: description: "Fetches one page and reports what it gets wrong.\n\nIt returns the page's on-page score, its title and\ \ description, how much readable\ntext it carries, and the full set of named checks \u2014 is it https, does it have\ \ one\nh1, is the title duplicated, is it slow, is it a redirect, is anything on it\nbroken. It is the technical half\ \ of search visibility, and it is the half a\ndeveloper can act on this afternoon.\n\nONE PAGE, LIVE, IN THIS REQUEST.\ \ It is deliberately not a site crawl: a crawl is\na job with a lifecycle, and this answers the same questions about\ \ the page\nsomebody is actually looking at, now, with no task id to poll. Point it at the\npages that matter one\ \ at a time.\n\nIt is priced per page fetched, which is one." operationId: seoAudit requestBody: content: application/json: schema: $ref: '#/components/schemas/seo.seoAuditIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/seo.seoAuditOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Fetch one page and report what it gets wrong tags: - seo x-app: seo x-public: true x-tool: true /v1/seo/backlinks: post: description: "Summarises who links to a target.\n\nIt returns the authority score, how many links point at it and from\ \ how many\ndistinct sites, how many of those are broken, and how much of the profile reads\nas spam. Distinct sites\ \ is the number to read: a thousand links from one domain\nis one endorsement, and a profile that grew fast in links\ \ and not in domains is\nusually a profile somebody bought.\n\nThe target can be a whole domain, a subdomain, or one\ \ page URL \u2014 the summary is\nscoped to whatever is named. It is priced per request, so a domain with ten\nmillion\ \ links costs the same as one with ten." operationId: seoBacklink requestBody: content: application/json: schema: $ref: '#/components/schemas/seo.seoBacklinkIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/seo.seoBacklinkOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Who links to a target, and how much of it is broken or spam tags: - seo x-app: seo x-public: true x-tool: true /v1/seo/competitors: post: description: 'Names the domains that place for the same phrases. Given a set of phrases it returns the sites that appear across them, with each one''s average position, how many of the phrases it places for, its share of the available attention and the visits that earns. It answers "who am I actually up against here", which is a different question from "who do I think my competitors are" and frequently a different answer. Pair it with seoRank: this says who is in the race, seoRank says where any one of them finishes. It is priced per row, so Limit decides the cost.' operationId: seoCompetitor requestBody: content: application/json: schema: $ref: '#/components/schemas/seo.seoCompetitorIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/seo.seoCompetitorOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: The domains that place for the same phrases tags: - seo x-app: seo x-public: true x-tool: true /v1/seo/ideas: post: description: "Grows a seed phrase into the phrases nobody named yet.\n\nIt takes phrases you have and returns phrases\ \ in the same category that you do\nnot \u2014 relevant rather than merely containing the seed \u2014 each with its\ \ search\nvolume, click cost, competition and how hard its first page is to reach. This is\nwhere a keyword list comes\ \ FROM; seoKeyword is where a list you already have gets\nmeasured.\n\nIt is priced per row, so Limit is the knob\ \ that decides what the call costs.\nTotal says how many more there were." operationId: seoIdea requestBody: content: application/json: schema: $ref: '#/components/schemas/seo.seoIdeaIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/seo.seoIdeaOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Grow a seed phrase into the phrases nobody named yet tags: - seo x-app: seo x-public: true x-tool: true /v1/seo/keywords: post: description: 'Measures phrases the caller already has. It answers, for each phrase named, how many people search it in a month, what an advertising click on it costs, and how contested that advertising is. This is the ground fact of search: everything else on this surface is a question about phrases, and this is the one that says whether a phrase is worth having. Give it phrases you already suspect. To find phrases you have not thought of, use seoIdea; to find the ones a site already places for, use seoRank. The market defaults to the United States in English. It is priced per request rather than per phrase, so asking about fifty phrases costs what asking about one does.' operationId: seoKeyword requestBody: content: application/json: schema: $ref: '#/components/schemas/seo.seoKeywordIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/seo.seoKeywordOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: How often named phrases are searched, and what a click costs tags: - seo x-app: seo x-public: true x-tool: true /v1/seo/rankings: post: description: "Reports every phrase a domain already places for.\n\nFor each one it gives the phrase, the position on\ \ the results page, the page of\nthe site that placed, that result's headline, the phrase's monthly searches and\n\ the visits the placement is estimated to earn. It is the single most direct\nquestion about a site's search visibility\ \ \u2014 yours or a competitor's, since it\ntakes any domain.\n\nPosition is the ABSOLUTE rank, counting every element\ \ on the page \u2014 the ads, the\nanswer boxes, the map \u2014 because that is what a person scrolling actually passes.\n\ An organic-only rank flatters a result that sits below half a screen of other\nthings.\n\nIt is priced per row, so\ \ Limit decides what the call costs, and Total says how\nmany more there were." operationId: seoRank requestBody: content: application/json: schema: $ref: '#/components/schemas/seo.seoRankIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/seo.seoRankOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Every phrase a domain already places for, with its position tags: - seo x-app: seo x-public: true x-tool: true /v1/seo/rates: get: description: "Publishes what every call on this surface costs.\n\nThe numbers are read from the upstream's own published\ \ price list, not from a\ntable kept here, so a price change on their side moves this card within the hour\nand moves\ \ what is debited with it. That is the whole of the pricing model: this\nsurface resells at cost, and the cost is\ \ theirs to state.\n\nA row has two numbers because a call has two costs: a flat charge for asking, and\na charge\ \ per row returned. An op priced per request reports zero for the second,\nand for one priced per row the total is\ \ `request + result x limit` \u2014 which is the\namount your balance is authorized against before the call, and roughly\ \ what you\nwill be debited after it.\n\nIt is a read and it is free: asking what something costs must not require\ \ the\nbalance that would pay for it. If the upstream cannot be reached the card comes\nback empty rather than stale\ \ \u2014 a price nobody can confirm is not a price." operationId: seoRate responses: '200': content: application/json: schema: $ref: '#/components/schemas/seo.seoRateOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: What each call on this surface costs, from the vendor's own list tags: - seo x-app: seo x-public: true x-tool: true /v1/settings/{product}: get: description: "Reads the caller org's configuration for one product, with every\nsecret field MASKED \u2014 only the\ \ names of the set secrets come back, never their\nvalues, which live in KMS. A product the org has never configured\ \ is not a 404:\nit answers 200 with an empty config object, so the console's Settings tab always\nrenders and merges\ \ its own display defaults on top." operationId: get_settings_by_product parameters: - description: Product is the catalog slug, from the path. Must match ^[a-z0-9][a-z0-9._-]{0,62}$. in: path name: product required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/settings.View' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Reads the caller org's configuration for one product, with every secret field MASKED \u2014 only the names\ \ of the set secrets come back, never their values, which live in KMS." tags: - settings x-app: settings x-public: true x-tool: true put: description: 'Writes the caller org''s configuration for one product and answers the stored result, secrets masked. Secret VALUES are sealed into KMS under orgs/{org}/settings/{product}/{key} and never touch this deployment''s database; with no KMS configured a write that carries any secret is refused whole (503) rather than dropping it or persisting it in the clear. A secret the body omits keeps its stored value, so a partial write never silently clears one.' operationId: put_settings_by_product parameters: - description: 'Product is the catalog slug, from the PATH. zip binds the path last, so the URL names the product being written whatever a body field claims.' in: path name: product required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/settings.settingsReq' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/settings.View' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Writes the caller org's configuration for one product and answers the stored result, secrets masked. tags: - settings x-app: settings x-public: true x-tool: true /v1/share: get: description: 'Returns the tunnel shares the caller''s org currently has open, across every environment that org has enabled. It is a READ and it degrades honestly: an unconfigured deployment, an org that has not provisioned yet, and an unreachable controller all answer an EMPTY list at 200 rather than an error, so the console never error-toasts on load.' operationId: get_share responses: '200': content: application/json: schema: $ref: '#/components/schemas/share.sharesOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the tunnel shares the caller's org currently has open, across every environment that org has enabled. tags: - share x-app: share x-public: true x-tool: true /v1/share/enable: post: description: 'Provisions the caller org''s tunnel account and returns the credential the `hanzo share` CLI needs to run a tunnel. It is idempotent: the account is keyed deterministically off the VALIDATED org, so a repeat call hands back the same account rather than creating a second one, and a caller can only ever provision their OWN org''s account. 503 when the deployment has no share controller configured; 502 when that controller is unreachable.' operationId: post_share_enable responses: '200': content: application/json: schema: $ref: '#/components/schemas/share.enableResp' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Provisions the caller org's tunnel account and returns the credential the `hanzo share` CLI needs to run a tunnel. tags: - share x-app: share x-public: true x-tool: true /v1/social/accounts: get: description: "Returns the org's connected accounts \u2014 each one's id, network,\nhandle, status and timestamps, most-recently-updated\ \ first.\n\nAn account's provider access token is NEVER included in any response on this\nsurface. Only the publisher\ \ reads it." operationId: get_social_accounts parameters: - description: "Provider keeps only accounts on one network \u2014 x, facebook, instagram,\nlinkedin, tiktok, youtube\ \ or threads. Omit it for every network. It is\nlower-cased and trimmed before it is matched, and a value that names\ \ no\nnetwork simply matches nothing rather than being refused." in: query name: provider required: false schema: type: string - description: 'Limit bounds the page, defaulting to 200 and capped at 1000. It is a string rather than an integer on purpose: the route parses it with a leading trim and falls back to the default on anything it cannot read, so `?limit=%2050` is a page of fifty today. An integer field would refuse the space and read an unparseable value as zero, which is a different page.' in: query name: limit required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/social.socialAccounts' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the org's connected accounts \u2014 each one's id, network, handle, status and timestamps, most-recently-updated\ \ first." tags: - social x-app: social x-public: true x-tool: true post: description: 'Records a social account for the org and answers 201 with the stored row, including the generated id later calls address it by.' operationId: post_social_accounts requestBody: content: application/json: schema: $ref: '#/components/schemas/social.socialAccountBody' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/social.socialAccount' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records a social account for the org and answers 201 with the stored row, including the generated id later calls address it by. tags: - social x-app: social x-public: true x-tool: true /v1/social/accounts/{id}: delete: description: "Removes one connected account from the org and answers 204 with no\nbody; an id that is not there is 404.\n\ \nIt removes the account record only. Posts that already published through it keep\ntheir published state and their\ \ recorded external ids \u2014 this does not retract\nanything from the network." operationId: delete_social_accounts_by_id parameters: - description: ID is the account or post to act on, taken from the path. in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one connected account from the org and answers 204 with no body; an id that is not there is 404. tags: - social x-app: social x-public: true x-tool: true get: description: "Returns one of the org's connected accounts by id \u2014 its network,\nhandle, status and timestamps \u2014\ \ or 404. The provider access token is not part of\nthe response." operationId: get_social_accounts_by_id parameters: - description: ID is the account or post to act on, taken from the path. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/social.socialAccount' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one of the org's connected accounts by id \u2014 its network, handle, status and timestamps \u2014\ \ or 404." tags: - social x-app: social x-public: true x-tool: true put: description: 'Replaces the account''s network, handle and status with what the body carries, and answers with the stored row. This is a REPLACEMENT, not a merge, which is the rule most easily got wrong: a field the body omits is written as its default, so leaving out the handle blanks it and leaving out the status resets it to connected. Send the whole record. The same vocabularies as create apply, and an unknown network or status is refused rather than coerced.' operationId: put_social_accounts_by_id parameters: - in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/social.socialAccountWrite' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/social.socialAccount' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Replaces the account's network, handle and status with what the body carries, and answers with the stored row. tags: - social x-app: social x-public: true x-tool: true /v1/social/posts: get: description: "Returns the org's posts \u2014 content, channel, status, scheduled time,\nmedia and timestamps \u2014\ \ most-recently-updated first." operationId: get_social_posts parameters: - description: "Status keeps only posts in one state \u2014 draft, scheduled, published or\nfailed. Omit it for every\ \ state. The transient publishing claim is not a\nuser-visible state and matching it is not useful." in: query name: status required: false schema: type: string - description: 'Limit bounds the page, defaulting to 200 and capped at 1000. A string for the same reason accountFilter.Limit is.' in: query name: limit required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/social.socialPosts' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the org's posts \u2014 content, channel, status, scheduled time, media and timestamps \u2014 most-recently-updated\ \ first." tags: - social x-app: social x-public: true x-tool: true post: description: "Stores a post for the org and answers 201 with the stored row.\n\nA post created as scheduled for a time\ \ that has already passed is published\nIMMEDIATELY, and the row returned carries that outcome \u2014 this is the\ \ one behaviour\na reader would otherwise miss. A future-scheduled post is left for the scheduler,\nand a draft is\ \ left alone. Publishing never fails the creation: the post is stored\neither way, and a publish that could not run\ \ leaves the row for the scheduler to\nretry." operationId: post_social_posts requestBody: content: application/json: schema: $ref: '#/components/schemas/social.socialPostBody' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/social.socialPost' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Stores a post for the org and answers 201 with the stored row. tags: - social x-app: social x-public: true x-tool: true /v1/social/posts/{id}: delete: description: 'Removes one post from the org and answers 204 with no body; an id that is not there is 404. It deletes the record here only. A post that has already published is not retracted from the network by deleting it.' operationId: delete_social_posts_by_id parameters: - description: ID is the account or post to act on, taken from the path. in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one post from the org and answers 204 with no body; an id that is not there is 404. tags: - social x-app: social x-public: true x-tool: true get: description: "Returns one of the org's posts by id, with its current status, scheduled\ntime, media and \u2014 once\ \ it has published \u2014 the account and external id it published\nunder. 404 when there is no such post for this\ \ org." operationId: get_social_posts_by_id parameters: - description: ID is the account or post to act on, taken from the path. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/social.socialPost' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one of the org's posts by id, with its current status, scheduled time, media and \u2014 once it has\ \ published \u2014 the account and external id it published under." tags: - social x-app: social x-public: true x-tool: true put: description: "Replaces the post's content, channel, status, scheduled time and media\nwith what the body carries, and\ \ answers with the stored row.\n\nA REPLACEMENT, not a merge: an omitted field is written as its default, so\nomitting\ \ media clears it and omitting the status resets the post to draft.\n`content` is required on every update. Unlike\ \ create, this never triggers a\npublish \u2014 moving a post's scheduled time into the past here leaves it for the\n\ scheduler; publish now is its own operation." operationId: put_social_posts_by_id parameters: - in: path name: id required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/social.socialPostWrite' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/social.socialPost' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Replaces the post's content, channel, status, scheduled time and media with what the body carries, and answers with the stored row. tags: - social x-app: social x-public: true x-tool: true /v1/social/posts/{id}/publish: post: description: 'Publishes the post immediately to the connected accounts on its channel and answers with the updated row, carrying the account and external id it published under. It is IDEMPOTENT: a post that has already published, or that another caller is publishing right now, comes back unchanged rather than being posted twice. That claim is taken before any network call, which is what makes a double submit safe. The two failure shapes differ on purpose. Having no connected account for the channel is the caller''s to fix, so it is recorded ON the post as failed with the reason and answers normally. A deployment that lacks the network''s own credentials cannot publish for anyone, so that is a 503 naming exactly what is missing.' operationId: post_social_posts_by_id_publish parameters: - description: ID is the account or post to act on, taken from the path. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/social.socialPost' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Publishes the post immediately to the connected accounts on its channel and answers with the updated row, carrying the account and external id it published under. tags: - social x-app: social x-public: true x-tool: true /v1/social/providers: get: description: "Reports each supported network's publish-readiness: whether this\ndeployment holds the OAuth application\ \ credentials for it and, when it does not,\nexactly which environment variables are missing.\n\nThis is a live read\ \ of the deployment's own configuration, not a static list of\nnetworks \u2014 it answers \"can I connect this today\"\ , which is what a connect\naffordance and a pre-cutover checklist both need. It says nothing about whether\nthe caller\ \ has connected an account; that is the accounts listing." operationId: get_social_providers responses: '200': content: application/json: schema: $ref: '#/components/schemas/social.socialProviders' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Reports each supported network''s publish-readiness: whether this deployment holds the OAuth application credentials for it and, when it does not, exactly which environment variables are missing.' tags: - social x-app: social x-public: true x-tool: true /v1/social/summary: get: description: 'Returns four counts for the caller''s org: total posts, how many are scheduled, how many have published, and how many accounts are connected. It is the dashboard roll-up, computed over the org''s own rows in one read.' operationId: get_social_summary responses: '200': content: application/json: schema: $ref: '#/components/schemas/social.socialSummary' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns four counts for the caller''s org: total posts, how many are scheduled, how many have published, and how many accounts are connected.' tags: - social x-app: social x-public: true x-tool: true /v1/space/health: get: description: "Reports whether this deployment can serve spaces, drives and files.\n\nIt is a REAL probe rather than\ \ a constant: 200 when object-store credentials\nare present, so the store is reachable in principle, and 503 with\ \ the reason\nwhen they are not. It is deliberately NOT gated \u2014 liveness has to be probe-able\nwithout a token\ \ \u2014 so it is the one operation here that names no space and bills\nnothing." operationId: get_space_health responses: '200': content: application/json: schema: $ref: '#/components/schemas/space.spaceHealth' description: ok '503': content: application/json: schema: $ref: '#/components/schemas/space.spaceHealth' description: service unavailable default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports whether this deployment can serve spaces, drives and files. tags: - space x-app: space x-public: true x-tool: true /v1/space/spaces: get: description: 'Lists the caller org''s own spaces. Only the caller''s: every space is physically named under a per-org prefix and the listing strips that prefix, so another org''s spaces are not in the answer at all. Another org''s space is not refused but INVISIBLE, so this cannot be used to learn that a name is taken elsewhere. Billed per call: the balance is checked BEFORE anything is touched, so an unfunded org is refused with nothing done, and the debit lands only once the work has succeeded.' operationId: get_space_spaces responses: '200': content: application/json: schema: $ref: '#/components/schemas/space.spaceList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller org's own spaces. tags: - space x-app: space x-public: true x-tool: true post: description: 'Makes a new space for the caller''s org and answers 201 with it. The one bucket a space''s files live in is derived from the caller''s VALIDATED org, so an org can only ever create inside its own namespace and no request field can redirect that. A name already taken in the org is 409. Billed per call: the balance is checked BEFORE anything is touched, so an unfunded org is refused with nothing created, and the debit lands only once the space exists.' operationId: post_space_spaces requestBody: content: application/json: schema: $ref: '#/components/schemas/space.spaceIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/space.spaceItem' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Makes a new space for the caller's org and answers 201 with it. tags: - space x-app: space x-public: true x-tool: true /v1/space/{space}/drives: get: description: "Lists a space's drives.\n\nListing the drives IS listing the space's root folder, because a drive is the\n\ first segment of a key and nothing else \u2014 so the two can never disagree the way\na drives table and the keys\ \ under it would. A space the caller's org does not\nown is the same 404 an unknown name gives.\n\nBilled per call:\ \ the balance is checked BEFORE anything is touched, so an\nunfunded org is refused with nothing read, and the debit\ \ lands only once the\nlisting has succeeded." operationId: get_space_by_space_drives parameters: - description: Space is the space's name, from the path. in: path name: space required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/space.driveList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists a space's drives. tags: - space x-app: space x-public: true x-tool: true post: description: "Makes a new drive in a space and answers 201 with it.\n\nA drive is a PREFIX and not a bucket, so making\ \ one writes a zero-byte marker\nat \"/\" \u2014 which is what makes an empty drive visible to a listing that\ \ has\nno other key to find. A name already taken in the space is 409.\n\nBilled per call: the balance is checked\ \ BEFORE anything is touched, so an\nunfunded org is refused with nothing created, and the debit lands only once the\n\ drive exists." operationId: post_space_by_space_drives parameters: - description: 'Space is the space to create the drive in, from the path. It carries NO `url:"-"`, unlike the field below it, and the difference is the whole reason both tags are written out: zip''s binder skips a field tagged "-" for EVERY URL source, path params included, so a path-borne value that carried it would arrive empty and the create would refuse a perfectly good address.' in: path name: space required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/space.driveIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/space.driveItem' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Makes a new drive in a space and answers 201 with it. tags: - space x-app: space x-public: true x-tool: true /v1/space/{space}/drives/{drive}: delete: description: 'Removes an EMPTY drive and answers 204. A drive holding files is 409 rather than a cascade: deleting an org''s files behind a single drive call is not a thing this surface will do silently. A drive or space the caller''s org does not own is the same 404 an unknown name gives. Billed per call: the balance is checked BEFORE anything is touched, so an unfunded org is refused with nothing deleted, and the debit lands only once the drive is gone.' operationId: delete_space_by_space_drives_by_drive parameters: - description: Space is the space's name, from the path. in: path name: space required: true schema: type: string - description: Drive is the drive's name, from the path. in: path name: drive required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes an EMPTY drive and answers 204. tags: - space x-app: space x-public: true x-tool: true /v1/space/{space}/drives/{drive}/files: get: description: "Lists one folder level of a drive.\n\nFolder-style by default: sub-folders come back as folder entries,\ \ which is the\nfile-manager view. `?recursive=true` lists every file flat under the folder\ninstead. Names are RELATIVE\ \ to `?folder=`, and the listing is bounded so a huge\ndrive cannot exhaust memory \u2014 Total is what came back,\ \ not what the drive holds.\n\nBilled per call: the balance is checked BEFORE anything is touched, so an\nunfunded\ \ org is refused with nothing read, and the debit lands only once the\nlisting has succeeded." operationId: get_space_by_space_drives_by_drive_files parameters: - description: Space is the space to list in, from the path. in: path name: space required: true schema: type: string - description: Drive is the drive to list, from the path. in: path name: drive required: true schema: type: string - in: query name: folder required: false schema: type: string - in: query name: recursive required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/space.fileList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists one folder level of a drive. tags: - space x-app: space x-public: true x-tool: true /v1/standing/upkeep: post: description: 'Reports what keeping this entity costs every year, itemised. This is the figure that decides where to incorporate, and the one a formation price cannot show: Delaware is cheaper to form than Wyoming for a corporation and dearer to keep, so a founder shown only the formation fee is shown the half that reverses. Each state line carries the authority that publishes it and the date it was checked, and a franchise tax that scales is marked a minimum rather than quoted as final.' operationId: post_standing_upkeep requestBody: content: application/json: schema: $ref: '#/components/schemas/standing.upkeepIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/standing.Upkeep' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports what keeping this entity costs every year, itemised. tags: - standing x-app: standing x-public: true x-tool: true /v1/sync: get: description: "Returns every sync link the caller's org has, each with its two endpoints, its\ndirection and trigger\ \ policy, and the time it last reconciled. Scoped to the\ncaller's own org \u2014 another tenant's links are structurally\ \ unreachable." operationId: get_sync responses: '200': content: application/json: schema: $ref: '#/components/schemas/sync.syncList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns every sync link the caller's org has, each with its two endpoints, its direction and trigger policy, and the time it last reconciled. tags: - sync x-app: sync x-public: true x-tool: true post: description: 'Declares a sync between two endpoints and returns it. It is an UPSERT: re-declaring the same source and target updates that link rather than piling up duplicates, so a console that re-submits is safe. The org comes from the validated principal, never from the request, so a sync can only ever bind endpoints inside the caller''s own org. A git source must be an https clone URL on the provider''s own host with no embedded credentials; a target left empty is derived as a native repository named after the source. With run=true the first reconcile is queued in the background, so a large initial import never blocks this response.' operationId: post_sync requestBody: content: application/json: example: run: true source: locator: https://github.com/acme/site provider: github schema: $ref: '#/components/schemas/sync.syncReq' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/sync.syncView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Declares a sync between two endpoints and returns it. tags: - sync x-app: sync x-public: true x-tool: true /v1/sync/{id}: delete: description: 'Removes one sync and tears down the outbound mirror it derived, answering 204. The teardown is the point: without it an unsynced repository would keep force-pushing to the upstream it is no longer linked to. Org-scoped, so another tenant''s id is the same 404 an unknown id gives.' operationId: delete_sync_by_id parameters: - description: ID is the sync to act on, from the path. example: sync_1 in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one sync and tears down the outbound mirror it derived, answering 204. tags: - sync x-app: sync x-public: true x-tool: true get: description: 'Returns one sync by id. It is org-scoped: an id belonging to another tenant is the same 404 an unknown id gives, so a probe learns nothing about what exists.' operationId: get_sync_by_id parameters: - description: ID is the sync to act on, from the path. example: sync_1 in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/sync.syncView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one sync by id. tags: - sync x-app: sync x-public: true x-tool: true patch: description: "Updates one sync's mutable policy \u2014 direction, trigger and actor \u2014 in place.\nThe endpoints\ \ and the kind are immutable: re-pointing a sync is a delete and a\ncreate, so a link can never silently start syncing\ \ somewhere else. A field the\nrequest omits is left as it was. Changing the direction immediately reconciles the\n\ derived outbound mirror, so turning push off stops the upstream being written to\nrather than merely recording the\ \ intent." operationId: patch_sync_by_id parameters: - description: ID is the sync to update, from the path. example: sync_1 in: path name: id required: true schema: type: string requestBody: content: application/json: example: direction: pull id: sync_1 schema: $ref: '#/components/schemas/sync.patchSyncIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/sync.syncView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Updates one sync's mutable policy \u2014 direction, trigger and actor \u2014 in place." tags: - sync x-app: sync x-public: true x-tool: true /v1/sync/{id}/run: post: description: "Reconciles one sync now \u2014 the manual re-sync, and the initial import for a link\ncreated without\ \ run=true. The work is handed to a bounded background worker and the\ncall answers 202 immediately, so a large mirror-in\ \ never holds the request open;\nqueued=true means accepted, not finished." operationId: post_sync_by_id_run parameters: - description: ID is the sync to act on, from the path. example: sync_1 in: path name: id required: true schema: type: string responses: '202': content: application/json: schema: $ref: '#/components/schemas/sync.syncQueued' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Reconciles one sync now \u2014 the manual re-sync, and the initial import for a link created without run=true." tags: - sync x-app: sync x-public: true x-tool: true /v1/task/board: get: description: "Returns a board's issues \u2014 work items with their column, priority,\nassignee, labels and schedule.\n\ \nWHICH board is a filter, not an address. Bound to a repository (the key from\nthe path) it is that project's board;\ \ left unbound it is the org's whole\nboard; narrowed by label it is a board smaller than any repository \u2014 which\ \ is\nthe only way an app that lives as a directory inside a shared repository can\nhave one. Every combination is\ \ the same rows through the same projection, so\nno two boards can disagree about what a column means.\n\nThe column\ \ is a LABEL on the forge, so the board and the forge web UI are the\nsame object seen twice: relabelling in either\ \ moves the card in both. A closed\nissue reads as done whatever its labels say." operationId: get_task_board parameters: - description: "Key is the project whose issues to list, from the path. EMPTY means every\nproject in the org \u2014\ \ the global board. It is a filter like the rest of\nthis struct rather than an address, which is what lets one\ \ op answer both\n\"this board\" and \"all the work\" without a second surface disagreeing with\nthe first about\ \ what a column is." in: query name: key required: false schema: type: string - description: 'Status keeps only issues in that board column: backlog, todo, in_progress, done or canceled. An unknown value is refused with 400.' in: query name: status required: false schema: type: string - description: 'Kind keeps only work items of that shape: issue, pr or epic. An unknown value is refused with 400.' in: query name: kind required: false schema: type: string - description: Repo keeps only issues bound to that git repository. in: query name: repo required: false schema: type: string - description: "Label keeps only issues carrying that label, compared case-insensitively.\n\nThis is how a board narrows\ \ to something SMALLER than a repository \u2014 the\none mechanism for it. An estate whose apps are directories\ \ inside one\nrepository (hanzoai/cloud carries ~140 of them) has no repository per app\nto address, so the app\ \ is a label: `label=app/meet` is the meet board.\nNothing is provisioned to make one exist; a board is the query." in: query name: label required: false schema: type: string - description: 'Source keeps only issues opened from that surface: team, git, crm, helpdesk, cms or agent. An unknown value is refused with 400.' in: query name: source required: false schema: type: string - description: "Scheduled keeps only issues that carry a date \u2014 a start, a due date or\nboth. This is the timeline's\ \ slice of the board: pass scheduled=true to\nget exactly the rows a gantt has somewhere to draw, instead of fetching\n\ every issue and discarding the undated ones client-side." in: query name: scheduled required: false schema: type: boolean responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/task.issueView' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns a board's issues \u2014 work items with their column, priority, assignee, labels and schedule." tags: - task x-app: task x-public: true x-tool: true /v1/task/issues: get: description: 'Answers across every project in the org. The org comes from the validated principal and never from the request: a caller able to name the org could read another tenant''s backlog, and a search is exactly the shape that would quietly return it.' operationId: get_task_issues parameters: - description: "Q matches an issue's title or description. A word from the issue, which is\nwhat someone remembers \u2014\ \ not its number, which is what they are looking up." in: query name: q required: false schema: type: string - description: 'Project narrows to one team key; "" searches every project in the org, which is the point of this op.' in: query name: project required: false schema: type: string - description: 'Status keeps one board column: backlog, todo, in_progress, done, canceled.' in: query name: status required: false schema: type: string - description: 'Kind keeps one shape: issue, pr, epic.' in: query name: kind required: false schema: type: string - description: Repo keeps issues bound to one git repository. in: query name: repo required: false schema: type: string - description: "Room keeps issues bound to one collaboration room, spelled\n\"_\" \u2014 the exact value\ \ GET /v1/meet/call answers with, so a\nchannel's call and its task list name the room the same way. This is the\n\ read a channel view runs to draw its own list; it spans every board of the\norg, because the work a channel is about\ \ is not confined to one board." in: query name: room required: false schema: type: string - description: 'Source keeps one origin: team, git, crm, helpdesk, cms, agent. "git" is how you ask for the mirrored GitHub issues specifically.' in: query name: source required: false schema: type: string - description: Assignee keeps issues held by one person. Pass "me" for yourself. in: query name: assignee required: false schema: type: string - description: "Limit caps the answer; 0 means the default, and anything above the ceiling\nis clamped rather than refused\ \ \u2014 a search that errors on being too broad\nteaches people to guess." in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/task.issueHits' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Answers across every project in the org. tags: - task x-app: task x-public: true x-tool: true /v1/task/projects: get: description: "Returns the boards of your org \u2014 the places your work actually\nis. The key addresses the board's\ \ issues.\n\nA BOARD IS A PLACE WORK IS, not an object somebody provisioned. So the list is\nassembled from the work\ \ itself: the repositories your org has filed issues on,\nplus the boards the index holds. A repository with nothing\ \ on it is not in the\nlist and is still perfectly addressable \u2014 GET /projects/ reads it and a\ncreate\ \ files into it \u2014 so nothing is lost by leaving it out.\n\nMeasured, which is why: reading the forge's whole\ \ repository inventory put 745\nboards here, of which all but a handful were vendored forks and mirrors\n(.github,\ \ .profile, DOMPurify, BoatAttack) that will never carry this org's\nwork. A list that long is not a list \u2014 the\ \ estate's real roadmap was in it\nsomewhere and no one could see it.\n\nThe forge half is the FORGE's answer for\ \ your own account, so two people in\none org can legitimately see different boards." operationId: get_task_projects responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/task.boardView' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the boards of your org \u2014 the places your work actually is." tags: - task x-app: task x-public: true x-tool: true post: description: "Answers 405. A task board IS a repository on this deployment's forge, so creating, renaming and deleting\ \ one is a FORGE operation carried out with FORGE permissions.\n\nOffering it here would put a second endpoint on\ \ the same object, guarded by this surface instead of by the forge \u2014 a weaker guard on the same thing. So the\ \ route exists and refuses, rather than 404ing: \"not this service's job\" and \"no such thing\" are different facts,\ \ and the body names the forge so a caller knows where the job IS done.\n\nWhat this surface DOES own is the work\ \ on a board: list the boards you can see, read and file their issues, move a card between columns, and read the schedule\ \ a milestone is. Those are the routes beside this one." operationId: post_task_projects summary: "Refused \u2014 a board is a repository on the forge" tags: - task x-app: task x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/task/projects/{key}: delete: description: "Answers 405. A task board IS a repository on this deployment's forge, so creating, renaming and deleting\ \ one is a FORGE operation carried out with FORGE permissions.\n\nOffering it here would put a second endpoint on\ \ the same object, guarded by this surface instead of by the forge \u2014 a weaker guard on the same thing. So the\ \ route exists and refuses, rather than 404ing: \"not this service's job\" and \"no such thing\" are different facts,\ \ and the body names the forge so a caller knows where the job IS done.\n\nWhat this surface DOES own is the work\ \ on a board: list the boards you can see, read and file their issues, move a card between columns, and read the schedule\ \ a milestone is. Those are the routes beside this one." operationId: delete_task_projects_by_key parameters: - in: path name: key required: true schema: type: string summary: "Refused \u2014 a board is a repository on the forge" tags: - task x-app: task x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). get: description: "Returns one board of your org by its key \u2014 the repository name.\n404 when your org has no repository\ \ under that key, or when your own forge\naccount cannot see it." operationId: get_task_projects_by_key parameters: - description: 'Key is the project''s org-unique handle: 2-8 uppercase alphanumerics starting with a letter ("ENG", "OPS2"). Matched case-insensitively.' in: path name: key required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/task.boardView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one board of your org by its key \u2014 the repository name." tags: - task x-app: task x-public: true x-tool: true patch: description: "Answers 405. A task board IS a repository on this deployment's forge, so creating, renaming and deleting\ \ one is a FORGE operation carried out with FORGE permissions.\n\nOffering it here would put a second endpoint on\ \ the same object, guarded by this surface instead of by the forge \u2014 a weaker guard on the same thing. So the\ \ route exists and refuses, rather than 404ing: \"not this service's job\" and \"no such thing\" are different facts,\ \ and the body names the forge so a caller knows where the job IS done.\n\nWhat this surface DOES own is the work\ \ on a board: list the boards you can see, read and file their issues, move a card between columns, and read the schedule\ \ a milestone is. Those are the routes beside this one." operationId: patch_task_projects_by_key parameters: - in: path name: key required: true schema: type: string summary: "Refused \u2014 a board is a repository on the forge" tags: - task x-app: task x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/task/projects/{key}/issues: get: description: "Returns a board's issues \u2014 work items with their column, priority,\nassignee, labels and schedule.\n\ \nWHICH board is a filter, not an address. Bound to a repository (the key from\nthe path) it is that project's board;\ \ left unbound it is the org's whole\nboard; narrowed by label it is a board smaller than any repository \u2014 which\ \ is\nthe only way an app that lives as a directory inside a shared repository can\nhave one. Every combination is\ \ the same rows through the same projection, so\nno two boards can disagree about what a column means.\n\nThe column\ \ is a LABEL on the forge, so the board and the forge web UI are the\nsame object seen twice: relabelling in either\ \ moves the card in both. A closed\nissue reads as done whatever its labels say." operationId: get_task_projects_by_key_issues parameters: - description: "Key is the project whose issues to list, from the path. EMPTY means every\nproject in the org \u2014\ \ the global board. It is a filter like the rest of\nthis struct rather than an address, which is what lets one\ \ op answer both\n\"this board\" and \"all the work\" without a second surface disagreeing with\nthe first about\ \ what a column is." in: path name: key required: true schema: type: string - description: 'Status keeps only issues in that board column: backlog, todo, in_progress, done or canceled. An unknown value is refused with 400.' in: query name: status required: false schema: type: string - description: 'Kind keeps only work items of that shape: issue, pr or epic. An unknown value is refused with 400.' in: query name: kind required: false schema: type: string - description: Repo keeps only issues bound to that git repository. in: query name: repo required: false schema: type: string - description: "Label keeps only issues carrying that label, compared case-insensitively.\n\nThis is how a board narrows\ \ to something SMALLER than a repository \u2014 the\none mechanism for it. An estate whose apps are directories\ \ inside one\nrepository (hanzoai/cloud carries ~140 of them) has no repository per app\nto address, so the app\ \ is a label: `label=app/meet` is the meet board.\nNothing is provisioned to make one exist; a board is the query." in: query name: label required: false schema: type: string - description: 'Source keeps only issues opened from that surface: team, git, crm, helpdesk, cms or agent. An unknown value is refused with 400.' in: query name: source required: false schema: type: string - description: "Scheduled keeps only issues that carry a date \u2014 a start, a due date or\nboth. This is the timeline's\ \ slice of the board: pass scheduled=true to\nget exactly the rows a gantt has somewhere to draw, instead of fetching\n\ every issue and discarding the undated ones client-side." in: query name: scheduled required: false schema: type: boolean responses: '200': content: application/json: schema: items: $ref: '#/components/schemas/task.issueView' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns a board's issues \u2014 work items with their column, priority, assignee, labels and schedule." tags: - task x-app: task x-public: true x-tool: true post: description: 'Opens a work item on the board, filed as YOU, where the repository lives: an issue on the deployment''s forge, or on GitHub for a repository the forge mirrors from there or does not hold at all. Answers the card with the issue''s address as `url`. The column and priority are written as LABELS, which is what makes the card and the issue the same object: someone relabelling in the web UI has moved your card. Asked for in a chat thread, the issue is bound to that thread, and what happens to it on GitHub is said there.' operationId: post_task_projects_by_key_issues parameters: - description: "Key is the board \u2014 the repository name, from the path. `owner/name`, or\n`github.com/owner/name`,\ \ names a repository on GitHub that no board on the\nforge stands for." in: path name: key required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/task.newIssue' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/task.issueView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Opens a work item on the board, filed as YOU, where the repository lives: an issue on the deployment''s forge, or on GitHub for a repository the forge mirrors from there or does not hold at all.' tags: - task x-app: task x-public: true x-tool: true /v1/task/projects/{key}/issues/{num}: get: description: "Returns ONE work item in full \u2014 its description included.\n\nThe list reads answer a board, and a\ \ board is a summary: the description is\nwhere the actual content of a work item lives \u2014 what an issue asks\ \ for, what\nan epic's acceptance criteria are \u2014 and no read on this surface returned it.\nThe address is the\ \ one PATCH already accepts, so an item you can move is now\nan item you can read.\n\nIt reads the forge directly\ \ rather than filtering the org fan-out, then falls\nback to the index for a board the forge has never heard of \u2014\ \ the same order,\nand the same reason, as GetProject: a row is one kind of thing however it came\nto exist, so a\ \ caller does not have to know which store it is in to fetch it." operationId: get_task_projects_by_key_issues_by_num parameters: - description: "Key is the board \u2014 the repository name, or an index board's key." in: path name: key required: true schema: type: string - description: Num is the issue's number on that board. in: path name: num required: true schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/task.issueView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns ONE work item in full \u2014 its description included." tags: - task x-app: task x-public: true x-tool: true patch: description: "Edits a work item \u2014 rename it, rewrite it, move it to another\ncolumn, re-prioritise it, hand it\ \ to somebody, or comment on it. Absent fields\nare left alone. An issue on GitHub is edited there.\n\nMOVING A CARD\ \ IS A RELABEL. The column lives in the forge's label set, so the\nmove replaces that set rather than writing a status\ \ column here that a\nforge-side change could contradict. Moving to `done` also CLOSES the issue,\nbecause a done\ \ card and an open issue are a contradiction.\n\nAssigning it to `agent` hands it to the coding agent: see `assignee`." operationId: patch_task_projects_by_key_issues_by_num parameters: - description: "Key is the board \u2014 the repository name, from the path." in: path name: key required: true schema: type: string - description: Num is the issue number on that repository, from the path. in: path name: num required: true schema: format: int64 type: integer requestBody: content: application/json: schema: $ref: '#/components/schemas/task.issueEdit' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/task.issueView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Edits a work item \u2014 rename it, rewrite it, move it to another column, re-prioritise it, hand it to somebody,\ \ or comment on it." tags: - task x-app: task x-public: true x-tool: true /v1/task/projects/{key}/issues/{num}/claim: post: description: "Takes an issue: it becomes yours and it moves to in_progress.\n\nThe holder is the CALLER, never an argument.\ \ \"Assign this to someone else\" is\na different act with different authority, and it already exists as a PATCH;\n\ conflating them would let anyone hand work to anyone by naming them.\n\nClaiming something already held by someone\ \ else is refused rather than\nsilently taken \u2014 two agents on one issue is the failure this prevents, and a\n\ claim that quietly wins a race is worse than one that says no." operationId: post_task_projects_by_key_issues_by_num_claim parameters: - in: path name: key required: true schema: type: string - in: path name: num required: true schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/task.issueHit' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Takes an issue: it becomes yours and it moves to in_progress.' tags: - task x-app: task x-public: true x-tool: true /v1/task/rooms/{room}: get: description: "Summarises one room's work.\n\nThe room is opaque here and is deliberately not resolved: this package\ \ cannot\nsay whether a room exists \u2014 apps/team owns that document \u2014 so an unknown room\nanswers an EMPTY\ \ board rather than a 404. That is the honest answer and the\nuseful one: a channel that has never had an item filed\ \ in it and a channel id\nthat was mistyped both have no work, and inventing a distinction would require\nthis surface\ \ to hold a second copy of the room list (HIP-0523 \xA72 forbids it,\nand it would drift the first time a room was\ \ renamed).\n\nTenancy is the validated principal's org and nothing else, so a caller cannot\nread another tenant's\ \ channel by naming its room." operationId: get_task_rooms_by_room parameters: - description: "Room is the room, spelled \"_\" \u2014 the same value\nGET /v1/meet/call answers with,\ \ so a channel's call and its work name the\nroom identically. From the path." in: path name: room required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/task.roomWork' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Summarises one room's work. tags: - task x-app: task x-public: true x-tool: true /v1/tax/filings: get: description: 'Lists the caller org''s IRS return exports and where each stands, without their files. Org admins only.' operationId: get_tax_filings responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.filingList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller org's IRS return exports and where each stands, without their files. tags: - tax x-app: tax x-public: true x-tool: true post: description: "Exports the caller org's return for a tax year and form, as the files the IRIS\nTaxpayer Portal takes,\ \ and records the export. Org admins of the payer only: the\nfiles carry TINs in full, so a SuperAdmin acting inside\ \ the org is refused here\nexactly as at the TIN read.\n\nIt carries every furnished (or owed) form of that year and\ \ kind not already in\nan earlier export that IRIS did not reject \u2014 a rejected submission filed\nnothing, so\ \ its forms go out again as originals. Originals are in one set of\nfiles and corrections of forms already exported\ \ in another, at most 250 records\na file, each written to every rule Pub 5717 \xA73.5 sets and checked against them\n\ before it is answered. The files carry full TINs: the export is on the audit\ntrail and the files are not stored,\ \ only rebuilt from the sealed forms when read\nagain.\n\nIt DOES NOT FILE WITH THE IRS. Submitting through IRIS needs\ \ the payer's own\nTransmitter Control Code and a person at the portal; the answer lists those\nsteps, and POST /v1/tax/filings/{id}/receipt\ \ records what IRIS answered." operationId: post_tax_filings requestBody: content: application/json: example: kind: 1099-NEC year: 2026 schema: $ref: '#/components/schemas/tax.exportIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/tax.Filing' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Exports the caller org's return for a tax year and form, as the files the IRIS Taxpayer Portal takes, and records the export. tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/filings/{id}: get: description: "Reads one IRS return export WITH its files, rebuilt from the sealed forms and\nproven identical to what\ \ was exported. The files carry full TINs: org admins of\nthe payer only \u2014 a SuperAdmin is refused \u2014 and\ \ every read is on the audit trail." operationId: get_tax_filings_by_id parameters: - description: ID is the filing id, "iris_"-prefixed. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.Filing' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads one IRS return export WITH its files, rebuilt from the sealed forms and proven identical to what was exported. tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/filings/{id}/receipt: post: description: "Records what IRIS answered for an exported return \u2014 the Receipt ID on\nsubmission, then the acknowledgment.\ \ Org admins only.\n\nThis is the payer's report of its own act at the IRIS portal, made under its\nown Transmitter\ \ Control Code; nothing here reaches IRIS, and a status is only\never what the payer recorded. Moves: exported \u2192\ \ submitted (with the Receipt ID\nand TCC) \u2192 accepted, accepted_with_errors or rejected." operationId: post_tax_filings_by_id_receipt parameters: - description: ID is the filing, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: receiptId: 2026-12345678901-abc123def status: submitted tcc: D1234 schema: $ref: '#/components/schemas/tax.receiptIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.Filing' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Records what IRIS answered for an exported return \u2014 the Receipt ID on submission, then the acknowledgment." tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/forms: get: description: "Lists the 1099s the caller org prepared as payer, oldest first, voided and\nsuperseded ones included \u2014\ \ a return that was furnished stays on the record.\nOrg admins only." operationId: get_tax_forms parameters: - description: Year narrows the list to one tax year; absent lists every year. in: query name: year required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.formList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the 1099s the caller org prepared as payer, oldest first, voided and superseded ones included \u2014\ \ a return that was furnished stays on the record." tags: - tax x-app: tax x-public: true x-tool: true post: description: "Prepares the caller org's 1099 drafts for a tax year, as the PAYER: one\n1099-NEC and/or 1099-MISC per\ \ payee whose reportable payments on Hanzo's rails\nreach the year's threshold, from the same derivation GET /v1/tax/payments\n\ answers. Org admins only.\n\nEach draft carries the payee as its W-9 names it \u2014 under a live grant, and\notherwise\ \ only its org id \u2014 its boxes, box 4, the due dates, the payments it\nsums, and backup withholding at 24% when\ \ the payer holds no TIN for the payee\n(IRC \xA73406(a)(1)(A)) \u2014 an uncertified W-9 is stated as a warning,\ \ not a\nwithholding, because these payments do not require the certification. Preparing\nagain refreshes a draft\ \ or reviewed form in place (a reviewed one goes back to\ndraft, to be reviewed again); one payee has at most one\ \ live form per kind and\nyear, so a second preparation racing the first is refused, never doubled. A form\nalready\ \ furnished is left alone and named in skipped \u2014 change it by correcting\nit. Nothing is furnished to anyone\ \ and nothing is filed with the IRS." operationId: post_tax_forms requestBody: content: application/json: example: year: 2026 schema: $ref: '#/components/schemas/tax.prepareIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.Prepared' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Prepares the caller org''s 1099 drafts for a tax year, as the PAYER: one 1099-NEC and/or 1099-MISC per payee whose reportable payments on Hanzo''s rails reach the year''s threshold, from the same derivation GET /v1/tax/payments answers.' tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/forms/{id}: get: description: 'Reads one 1099 the caller org prepared as payer, every box and both parties, TINs masked. Org admins only; another org''s form is not found.' operationId: get_tax_forms_by_id parameters: - description: ID is the form id, "f1099_"-prefixed. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.Form' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads one 1099 the caller org prepared as payer, every box and both parties, TINs masked. tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/forms/{id}/correct: post: description: 'Corrects a furnished (or owed) 1099 with a NEW form that supersedes it. Org admins only. The furnished form is never changed: the correction is a second form, marked CORRECTED, carrying the original''s account number and naming the form it supersedes, and it goes through review and furnishing like any other. State the corrected boxes, or omit them to derive the year again from the payments and the payee''s current W-9. A form already superseded is corrected by correcting its successor. It files nothing with the IRS.' operationId: post_tax_forms_by_id_correct parameters: - description: ID is the furnished form to correct, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: boxes: - box: 1a cents: 250000 reason: a December settlement was refunded schema: $ref: '#/components/schemas/tax.correctIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/tax.Form' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Corrects a furnished (or owed) 1099 with a NEW form that supersedes it. tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/forms/{id}/furnish: post: description: "Furnishes Copy B of a reviewed 1099 to its payee. Org admins only.\n\nWhen the payee's tax profile consents\ \ to electronic delivery, Copy B is\ndelivered into the payee's /v1/tax/inbox and the form is furnished. Otherwise\ \ it\nis OWED on paper at the payee's address: mailing it is the payer's, and\nPOST /v1/tax/forms/{id}/mailed records\ \ it. Either way the statement is final\nfrom this call \u2014 the form can no longer change, only be corrected. Calling\ \ it\nagain on a form furnished electronically re-delivers Copy B if the inbox lacks\nit, and changes nothing else.\n\ \nIt does not file anything with the IRS, and it does not send the notice of\nposting Pub 1099 Part M asks a furnisher\ \ to give an electronic recipient; the\nstatement is in the recipient's inbox, and telling them so is the payer's." operationId: post_tax_forms_by_id_furnish parameters: - description: ID is the form id, "f1099_"-prefixed. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.Form' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Furnishes Copy B of a reviewed 1099 to its payee. tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/forms/{id}/mailed: post: description: 'Records that the payer mailed a paper Copy B it owed. Org admins only. Mailing is the payer''s act, done outside Hanzo; this is the record of it, made by the person who did it and kept on the audit trail. Nothing here mails anything.' operationId: post_tax_forms_by_id_mailed parameters: - description: ID is the form id, "f1099_"-prefixed. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.Form' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records that the payer mailed a paper Copy B it owed. tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/forms/{id}/pdf: get: description: "Answers one 1099 the caller org prepared as payer as a one-page PDF \u2014 `application/pdf` under a Content-Disposition\ \ naming the form \u2014 rendered from the form's own content: the payer's copy by default, or Copy B (For Recipient)\ \ with `?copy=B` for a statement owed on paper. The recipient's TIN is truncated; the payer's prints in full, as the\ \ law requires on every form.\n\nThe render is a pure function of the form, so the same form renders the same bytes\ \ every time. Org admins only; another org's form is not found. It files nothing and mails nothing." operationId: get_tax_forms_by_id_pdf parameters: - in: path name: id required: true schema: type: string summary: Download a 1099 the org prepared, as a PDF tags: - tax x-app: tax x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/tax/forms/{id}/review: post: description: "Marks a draft 1099 reviewed \u2014 a person has checked it. Only a reviewed form can\nbe furnished. Org\ \ admins only." operationId: post_tax_forms_by_id_review parameters: - description: ID is the form id, "f1099_"-prefixed. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.Form' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Marks a draft 1099 reviewed \u2014 a person has checked it." tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/forms/{id}/void: post: description: 'Voids a draft or reviewed 1099, which is then never furnished or filed. A form already furnished cannot be voided: it is corrected, and a correction to zero is how a furnished return is withdrawn (Pub 1099 Part H). Org admins only.' operationId: post_tax_forms_by_id_void parameters: - description: ID is the form id, "f1099_"-prefixed. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.Form' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Voids a draft or reviewed 1099, which is then never furnished or filed. tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/inbox: get: description: "Lists the Copy B statements furnished to the caller org \u2014 every 1099 a payer on\nHanzo delivered\ \ to it electronically, corrected ones beside the ones they\nsupersede. Any member may read it; nothing here can be\ \ changed or removed." operationId: get_tax_inbox parameters: - description: Year narrows the list to one tax year; absent lists every year. in: query name: year required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.statementList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the Copy B statements furnished to the caller org \u2014 every 1099 a payer on Hanzo delivered to it\ \ electronically, corrected ones beside the ones they supersede." tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/inbox/{id}: get: description: 'Reads one Copy B furnished to the caller org. Any member may read it; another org''s statement is not found.' operationId: get_tax_inbox_by_id parameters: - description: ID is the form id, "f1099_"-prefixed. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.Statement' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads one Copy B furnished to the caller org. tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/inbox/{id}/pdf: get: description: "Answers one Copy B furnished to the caller org as a one-page PDF \u2014 `application/pdf` under a Content-Disposition\ \ naming the form \u2014 rendered from the statement exactly as it was furnished, which can never change. The recipient's\ \ own TIN is truncated; the payer's prints in full.\n\nAny member of the recipient org may download it; another org's\ \ statement is not found." operationId: get_tax_inbox_by_id_pdf parameters: - in: path name: id required: true schema: type: string summary: Download a Copy B furnished to the org, as a PDF tags: - tax x-app: tax x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/tax/payments: get: description: "Returns the caller org's tax year as a PAYER: every payment it made to another\norg in that calendar year,\ \ on any rail, as the event plane records it, each\nclassified \u2014 reportable or not, in which 1099 box, and the\ \ rule that decided it,\nwith its source \u2014 and each payee's totals tested against the year's threshold.\n\nThe\ \ rules applied: a payment settled by a card or a third-party settlement\nnetwork is reported on Form 1099-K by the\ \ settlement entity, not here; a payment\nfor merchandise is not reported; a payment to a corporation is generally\ \ not\nreported, except for legal services; a payment in a digital asset is reported at\nits fair market value on\ \ the date paid; a box is reported only at or above the\nyear's threshold \u2014 $600 through 2025, $2,000 for 2026\ \ under P.L. 119-21, and not\nguessed for a year the IRS has not yet published. A payee's classification comes\nonly\ \ from a W-9 it granted the caller; without one, nothing establishes an\nexemption.\n\nOrg admins only. It reads;\ \ it prepares nothing and files nothing." operationId: get_tax_payments parameters: - description: Year is the calendar year the payments were made in, e.g. 2026. in: query name: year required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.Ledger' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the caller org's tax year as a PAYER: every payment it made to another org in that calendar year,\ \ on any rail, as the event plane records it, each classified \u2014 reportable or not, in which 1099 box, and the\ \ rule that decided it, with its source \u2014 and each payee's totals tested against the year's threshold." tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/profile: get: description: "Returns the caller org's own tax profile \u2014 every line of its Form W-9, W-8BEN or\nW-8BEN-E, every\ \ number masked to its last four characters, where the\ncertification stands, whether a W-8 is valid and when it expires,\ \ and whether\nthe org consents to receive its 1099s electronically.\n\nAny member may read it. 404 until an admin\ \ writes one. No full number is ever in\nthis answer." operationId: get_tax_profile responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.Profile' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the caller org's own tax profile \u2014 every line of its Form W-9, W-8BEN or W-8BEN-E, every number\ \ masked to its last four characters, where the certification stands, whether a W-8 is valid and when it expires,\ \ and whether the org consents to receive its 1099s electronically." tags: - tax x-app: tax x-public: true x-tool: true put: description: "Writes the caller org's tax profile \u2014 its Form W-9, or its Form W-8BEN or\nW-8BEN-E when it is a\ \ foreign person \u2014 and answers it, every number masked. Org\nadmins only.\n\nA U.S. TIN is validated before it\ \ is kept \u2014 nine digits; an EIN's prefix must be\none the IRS assigns; an SSN never begins 000, 666 or 9xx and\ \ has no zero group\nor serial; an ITIN's middle digits are in the IRS ranges. A foreign TIN is the\nissuing jurisdiction's\ \ and is checked only for its characters. Every number is\nsealed under this org's own data key before it is written.\ \ Validating the\nFORMAT is all this does: whether the IRS has the number on file under this name\nis IRS TIN Matching,\ \ which a payer runs through IRS e-Services.\n\nA W-8 is refused where the form itself says it is the wrong one: a\ \ U.S. citizen,\na U.S. permanent residence or an entity organized in the United States is a U.S.\nperson and files\ \ a W-9. A treaty claim needs a U.S. or foreign TIN on the form\n(Instructions for Form W-8BEN, line 5; for Form W-8BEN-E,\ \ line 8), and names the\ncountry of the permanent residence address.\n\nChanging any fact on the form makes a new\ \ version, and the certification no\nlonger covers it: certify again. For a W-8 that is the \"change in circumstances\"\ \nthe instructions end its validity on. Changing only the consent does not." operationId: put_tax_profile requestBody: content: application/json: example: address: city: San Francisco line1: 1 Market St state: CA zip: '94105' classification: c_corp electronicConsent: true name: Acme Robotics, Inc. tin: 12-3456789 tinType: ein schema: $ref: '#/components/schemas/tax.profileIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.Profile' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Writes the caller org's tax profile \u2014 its Form W-9, or its Form W-8BEN or W-8BEN-E when it is a foreign\ \ person \u2014 and answers it, every number masked." tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/profile/certify: post: description: "Signs the caller org's certification \u2014 Form W-9 Part II, Form W-8BEN Part III or\nForm W-8BEN-E Part\ \ XXX \u2014 through /v1/legal's e-signature. The org's own admins\nonly, and the admin is the signer: a SuperAdmin\ \ acting inside the org is not a\nperson who can certify for it.\n\nThe first call renders the certification \u2014\ \ the IRS's own certification text over\nthis profile's facts, every number truncated \u2014 as a /v1/legal document\ \ and opens\nits signature request with the calling admin as signer; the answer is `pending` with\nthe document id.\ \ The signer completes it where every /v1/legal signature is\ncompleted (POST /v1/legal/documents/{id}/sign/complete).\ \ Calling this again then\nasks legal whether the document is signed and by whom: signed by the signer, the\nprofile\ \ is recorded certified; not yet signed, it answers `pending` again; signed\nby anyone else \u2014 another member,\ \ a key \u2014 or voided, it is not the signer's\ncertification, and a fresh request is opened in its place.\n\nA\ \ certification covers one profile version: writing any fact on the form\nafterwards voids it. A certified W-8 is\ \ valid through the last day of the third\ncalendar year after the year it was signed (Instructions for Form W-8BEN\ \ and\nW-8BEN-E, Rev. 10-2021); certifying it again after that renews it. It is not a\nstatement that a TIN matches\ \ IRS records \u2014 that is IRS TIN Matching, which only\na payer enrolled in IRS e-Services can run." operationId: post_tax_profile_certify responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.Profile' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Signs the caller org's certification \u2014 Form W-9 Part II, Form W-8BEN Part III or Form W-8BEN-E Part XXX\ \ \u2014 through /v1/legal's e-signature." tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/w9: get: description: 'Lists the caller org''s W-9 relationships on both sides: the W-9s it asked its payees for, with where each stands, and the requests other orgs made for its own W-9. Org admins only. No W-9 content is in the list; read one to see it.' operationId: get_tax_w9 responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.w9List' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Lists the caller org''s W-9 relationships on both sides: the W-9s it asked its payees for, with where each stands, and the requests other orgs made for its own W-9.' tags: - tax x-app: tax x-public: true x-tool: true post: description: "Asks another org for its W-9, as the org that pays it. Org admins only.\n\nA request is refused unless\ \ the caller has paid that org on Hanzo's rails, or\nhas asked /v1/principal whether it may \u2014 a W-9 is asked\ \ for by a payer, of a payee,\nand the payments, or a payment the caller is about to make, are what say which\ncompanies\ \ those are. The form is needed BEFORE the first payment: withholding\nis decided when a payment is made, not when\ \ it is reported. The request lands with the payee, whose admin grants or\ndeclines it; until it is granted the caller\ \ sees its status and nothing of the\npayee's W-9. Asking again updates the category, and re-opens a request the\n\ payee declined or revoked.\n\nCategory is the caller's statement of what it pays this payee for, and decides\nthe\ \ 1099 box: services and attorney go to 1099-NEC box 1a, rents, royalties\nand other to 1099-MISC boxes 1, 2 and 3;\ \ merchandise is not reported." operationId: post_tax_w9 requestBody: content: application/json: example: category: services payee: acme schema: $ref: '#/components/schemas/tax.w9Request' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/tax.W9' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Asks another org for its W-9, as the org that pays it. tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/w9/{id}: get: description: "Reads one W-9 relationship from the caller's side. Org admins only.\n\nAs the PAYER, the answer carries\ \ the payee's W-9 \u2014 every line, the TIN masked\nto its last four digits, and whether it is certified \u2014 but\ \ only while the\npayee's grant is live; before it is granted, and after it is declined or\nrevoked, there is no W-9\ \ in the answer. As the PAYEE, it is the request and\nwhere it stands." operationId: get_tax_w9_by_id parameters: - description: ID is the relationship's id, "w9_"-prefixed. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.W9' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads one W-9 relationship from the caller's side. tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/w9/{id}/decline: post: description: 'Declines a request for this org''s W-9. The payer keeps seeing only the status. Org admins of the payee only.' operationId: post_tax_w9_by_id_decline parameters: - description: ID is the relationship's id, "w9_"-prefixed. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.W9' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Declines a request for this org's W-9. tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/w9/{id}/grant: post: description: "Grants the payer that asked a live read of this org's W-9 \u2014 every line, the TIN\nmasked, and the\ \ full TIN to that payer's org admins alone, each read audited.\nOrg admins of the payee only. Needs a tax profile\ \ to grant." operationId: post_tax_w9_by_id_grant parameters: - description: ID is the relationship's id, "w9_"-prefixed. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.W9' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Grants the payer that asked a live read of this org's W-9 \u2014 every line, the TIN masked, and the full\ \ TIN to that payer's org admins alone, each read audited." tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/w9/{id}/match: post: description: 'Records the result of IRS TIN Matching for a payee, as the payer. Org admins of the payer only, on a live grant. It does not run TIN Matching. That is an IRS e-Services program a payer enrolls in and runs itself, on the name and TIN it holds; this records what the IRS answered so it sits beside the W-9 and on the audit trail. Nothing here ever records a match the payer did not report.' operationId: post_tax_w9_by_id_match parameters: - description: ID is the relationship, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: result: matched schema: $ref: '#/components/schemas/tax.matchIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.W9' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records the result of IRS TIN Matching for a payee, as the payer. tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/w9/{id}/revoke: post: description: "Revokes a W-9 this org granted. From the moment it is revoked the payer reads\nnothing of the W-9 and\ \ no full TIN. A 1099 the payer already prepared keeps the\nTIN it was prepared with \u2014 the payer must keep it\ \ to file \u2014 and that TIN is\nnever answered in full outside the payer's IRS filing export. Org admins of the\n\ payee only." operationId: post_tax_w9_by_id_revoke parameters: - description: ID is the relationship's id, "w9_"-prefixed. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.W9' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Revokes a W-9 this org granted. tags: - tax x-app: tax x-public: true x-tool: true /v1/tax/w9/{id}/tin: get: description: "Reads a payee's TIN in full \u2014 and, for a payee on a W-8, its foreign TIN, which\nthe payer's Form\ \ 1042-S carries. Only an org admin of the PAYER, only while the\npayee's grant is live, and every read is on the\ \ audit trail before a number is\nanswered \u2014 a read the trail cannot record is refused.\n\nThis is the one place\ \ a TIN leaves the platform in full, apart from the IRS\nfiling export. SuperAdmin is not admitted: platform authority\ \ is not a reason\nto read a company's TIN." operationId: get_tax_w9_by_id_tin parameters: - description: ID is the relationship's id, "w9_"-prefixed. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tax.tinOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Reads a payee's TIN in full \u2014 and, for a payee on a W-8, its foreign TIN, which the payer's Form 1042-S\ \ carries." tags: - tax x-app: tax x-public: true x-tool: true /v1/taxonomy: get: description: "Returns the product catalogue as this caller sees it: the PLATFORM\ncatalogue \u2014 Hanzo's own products,\ \ the part that is true for everyone \u2014 plus the\ncaller's own org's rows, every category in display order and\ \ each carrying the\nproducts filed under it in theirs. Another customer's rows are never in it. It\nis readable signed\ \ out, and a signed-out visitor gets the platform catalogue\nalone, which is what the marketing landing renders from.\n\ \nWhere the caller's org and the platform hold the same id, the caller's own row\nis the one served. That rule exists\ \ because ids are unique per ORG and not\nglobally \u2014 two customers may each have a \"crm\", and refusing the\ \ second would\ntell one of them the other exists \u2014 so a collision with the platform is possible\nby construction\ \ and something has to win deterministically. Yours does: your own\ncatalogue is the one you edited.\n\n`?brand=`\ \ narrows it the way a brand's own console does: only the categories\nthat brand admits, and within them only the\ \ taxa scoped to it. An unpublished\nrow is served only to whoever may edit it \u2014 a SuperAdmin for the platform's,\ \ an\norg admin for their own \u2014 so a product can be staged before anyone sees it\nwithout becoming invisible\ \ to the person staging it." operationId: get_taxonomy parameters: - description: "Brand returns only what that brand's console shows \u2014 the categories it\nadmits, and within them\ \ the taxa scoped to it. Empty returns everything." in: query name: brand required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/taxonomy.Taxonomy' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the product catalogue as this caller sees it: the PLATFORM catalogue \u2014 Hanzo's own products,\ \ the part that is true for everyone \u2014 plus the caller's own org's rows, every category in display order and\ \ each carrying the products filed under it in theirs." tags: - taxonomy x-app: taxonomy x-public: true x-tool: true /v1/taxonomy/categories/{id}: delete: description: "Removes one empty category. A category that still has taxa\nfiled under it is refused with 409 and a count:\ \ deleting the label off a group\nmust never silently take the products wearing it, and the alternative \u2014 orphan\n\ rows naming a category that no longer exists \u2014 is a catalogue that cannot be\nrendered. Move or delete its taxa\ \ first. An id no category holds is a 404." operationId: delete_taxonomy_categories_by_id parameters: - description: ID is the slug to act on, from the path. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/taxonomy.deleted' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one empty category. tags: - taxonomy x-app: taxonomy x-public: true x-tool: true put: description: "Creates or replaces one category and returns it as stored. The id in\nthe URL is the one it is filed under\ \ whatever the body says, so a category can\nnever be written under a name it was not addressed by \u2014 which also\ \ makes create\nand replace the same act, and is why there is no POST beside this.\n\nPlatform SuperAdmin only: one\ \ catalogue serves every tenant, so an org admin who\ncould rename a category would rename it for all of them." operationId: put_taxonomy_categories_by_id parameters: - description: ID is the category slug to write, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: label: Observe order: 6 summary: Traces, metrics, logs and alerts. schema: $ref: '#/components/schemas/taxonomy.categoryIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/taxonomy.Category' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates or replaces one category and returns it as stored. tags: - taxonomy x-app: taxonomy x-public: true x-tool: true /v1/taxonomy/taxa/{id}: delete: description: 'Removes one product from the catalogue. An id no taxon holds is a 404. To take a product out of view without losing what was written about it, set `published` to false instead.' operationId: delete_taxonomy_taxa_by_id parameters: - description: ID is the slug to act on, from the path. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/taxonomy.deleted' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one product from the catalogue. tags: - taxonomy x-app: taxonomy x-public: true x-tool: true put: description: "Creates or replaces one product and returns it as stored. The id in the\nURL is the one it is filed under\ \ whatever the body says. The category must\nalready exist \u2014 a taxon naming a category that does not is refused\ \ with 400\nrather than stored where nothing can render it.\n\nA taxon opens exactly one way: `route` for a product\ \ the console renders\nitself, or `href` for one that genuinely lives at its own domain. Giving both,\nor neither,\ \ is refused.\n\nPlatform SuperAdmin only." operationId: put_taxonomy_taxa_by_id parameters: - description: ID is the taxon slug to write, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: category: data description: Managed vector search. icon: Database name: Vector order: 3 route: /vector tags: - search schema: $ref: '#/components/schemas/taxonomy.taxonIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/taxonomy.Taxon' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates or replaces one product and returns it as stored. tags: - taxonomy x-app: taxonomy x-public: true x-tool: true /v1/team/account: post: description: "The account control plane the Team client speaks: one POST carries a `method` verb and its `params`, and\ \ answers {\"result\": \u2026}. The verbs are the session's own reads and the space switch \u2014 getLoginInfoByToken,\ \ getUserWorkspaces, selectWorkspace, getWorkspaceInfo, getMemberships, getPerson, getSocialIds, getRegionInfo, isReadOnlyGuest\ \ \u2014 plus sendInvite, which adds a member to a space and is refused for a caller who is not its owner or admin.\n\ \nA REFUSAL IS HTTP 200 carrying {\"error\": {severity, code, params}} \u2014 the platform Status the client translates\ \ \u2014 not a 4xx. An unreadable body, an unauthorized session and an unknown verb all arrive that way, so a caller\ \ that reads only the status code reads every failure here as a success.\n\nNO CREDENTIAL IS EVER HANDLED HERE. login,\ \ signUp, the OTP verbs, password change and reset, join and the guest-token exchange each answer Unauthorized with\ \ \"sign in at hanzo.id\" \u2014 a stated policy, not an unknown method, so the refusal is a fact a test can pin.\ \ Sessions come from the OAuth pair under /account/auth.\n\nAuth is the team session token: Authorization: Bearer,\ \ else the HttpOnly account-token cookie. The tenant is that token's SIGNED org claim, never a header, and selectWorkspace\ \ resolves only among the orgs the token proves membership of. It also demands an explicit workspaceUrl \u2014 it\ \ never falls back to a first space, and a slug that resolves in two of the caller's orgs answers Ambiguous rather\ \ than picking one." operationId: post_team_account summary: Read the caller's account and switch space tags: - team x-app: team x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/team/account/auth/{provider}: get: description: "STARTS the OAuth hop: answers 302 to hanzo.id's authorize endpoint and sets the short-lived HttpOnly state\ \ cookie that binds the flow to this browser. NO TOKEN COMES BACK FROM THIS CALL \u2014 the session is minted by the\ \ callback below, and a client that expects JSON here gets a redirect with no body.\n\nA browser is the intended caller.\ \ Anything else must follow the Location AND keep the Set-Cookie, because the callback refuses a flow whose state\ \ it cannot match. That cookie carries the random nonce plus the client's navigateUrl, so the round trip needs no\ \ second channel, and it lives ten minutes \u2014 the whole budget for the hop.\n\nThe provider segment only picks\ \ a hint: the redirect_uri is ALWAYS the canonical openid callback, the one IAM has registered. Measured end to end,\ \ hanzo.id strips that hint today, so /auth/google and /auth/openid land on the same Hanzo sign-in page \u2014 the\ \ federation shortcut is an upstream fix, not a second endpoint here." operationId: get_team_account_auth_by_provider parameters: - in: path name: provider required: true schema: type: string summary: Start a sign-in at hanzo.id tags: - team x-app: team x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/team/account/auth/{provider}/callback: get: description: "COMPLETES the OAuth hop: hanzo.id redirects the browser here with ?code and ?state, and the answer is\ \ another 302 \u2014 back to the client's login route carrying the freshly minted team session token in the query.\ \ Never JSON, and never a token in this response's own body.\n\nTHE STATE IS CHECKED FIRST, before the code is even\ \ looked at: the flow cookie is read and cleared one-shot, and a callback whose ?state does not equal the nonce it\ \ held is bounced with error=state_mismatch and NEVER exchanged. That is what makes a forged or replayed callback\ \ inert. Only then is the code exchanged server-side \u2014 team is a confidential client with a client_secret, so\ \ there is no PKCE and the code never passes through the browser's JS.\n\nThe tenant is derived from the IAM access\ \ token VERIFIED RS256 against the JWKS, the same trust anchor the identity boundary uses; a token whose owner claim\ \ is empty fails closed with no login at all. Every org that token proves gets a space ensured, so a member of two\ \ orgs is a counted seat in both. The IAM access token is also parked in an HttpOnly cookie for the same-origin agents\ \ proxy \u2014 page JS never reads it.\n\nEVERY failure is a redirect, not a status: a denied consent, a missing code,\ \ a failed exchange, an unreadable userinfo, an unverifiable org and a token-mint failure each bounce to the login\ \ page with an ?error code naming the step." operationId: get_team_account_auth_by_provider_callback parameters: - in: path name: provider required: true schema: type: string summary: Complete a sign-in and hand the browser its session tags: - team x-app: team x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/team/account/cookie: delete: description: "Signs this browser out of team by expiring the HttpOnly\naccount-token cookie the OAuth callback set.\ \ It is the counterpart of the\ncookie PUT, it takes nothing \u2014 the cookie it clears is named by this service,\n\ never by the caller \u2014 and it is unconditional: a caller with no cookie, an\nexpired one or a forged one all get\ \ the same acknowledgement, because clearing\nsomething that is not there is the same outcome as clearing something\ \ that is.\n\nIt clears ONLY the team session cookie. The IAM access-token cookie the same\ncallback set is a different\ \ credential with a different lifetime and is left\nalone, so this is a team sign-out, not a platform one." operationId: delete_team_account_cookie responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.cookieAck' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Signs this browser out of team by expiring the HttpOnly account-token cookie the OAuth callback set. tags: - team x-app: team x-public: true x-tool: true put: description: "Writes the team session token into the HttpOnly `account-token` cookie \u2014 Secure, SameSite=Lax, whole-origin\ \ scope, thirty days \u2014 and answers {\"result\": true}. This is how the client turns the token it caught off the\ \ OAuth bounce into a credential page JS can no longer read, which IS the security property: script that cannot see\ \ the cookie cannot exfiltrate it, and every later call on the files, billing and collaborator planes authenticates\ \ from it when no bearer is sent.\n\nThe token is VERIFIED \u2014 signature and expiry, against this service's own\ \ signing secret \u2014 BEFORE it is stored. Anything this service did not sign is 401 and nothing is written; persisting\ \ a caller-supplied value unchecked would be a session-fixation hole, where an attacker pins a cookie the victim's\ \ browser then presents as its own.\n\nThe token may arrive as `token` in the JSON body or, when the body is absent\ \ or unparseable, from the Authorization bearer \u2014 an unreadable body is NOT an error here. The sibling DELETE\ \ clears this same cookie and signs the browser out of team only: the IAM cookie set alongside it is a different credential\ \ with its own lifetime and is left alone." operationId: put_team_account_cookie responses: 2XX: content: application/json: schema: $ref: '#/components/schemas/cookieAck' description: Success summary: Store the session token as this browser's cookie tags: - team x-app: team x-public: true /v1/team/account/providers: get: description: "Returns the identity providers this deployment starts a login\nwith. It is always exactly one \u2014 hanzo.id.\ \ Which identities that provider accepts\n(Google, GitHub, passkey, password) is IAM's question, answered on IAM's\ \ own\npage next to the identity check and the training-data consent that must\nprecede a first session; listing them\ \ here would be a second place holding\nthat answer, and the two drift the moment IAM gains or drops one." operationId: get_team_account_providers responses: '200': content: application/json: example: - displayName: Hanzo name: openid schema: items: $ref: '#/components/schemas/team.ProviderInfo' type: array description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the identity providers this deployment starts a login with. tags: - team x-app: team x-public: true x-tool: true /v1/team/billing/plan: get: description: "Returns the plan and seat counts for the caller's OWN org, resolved\nfrom the VERIFIED team session token\ \ \u2014 never a client header. Seats and guests\nare the org's distinct active human members (a bot member is not\ \ a seat); the\nplan comes from the licensing entitlement and is empty when that read is\nunavailable, so the page\ \ shows an honest dash rather than a fabricated tier. A\ncaller with no verified session gets 401, and a real seat-read\ \ failure is a\n502 rather than a false \"0 members\"." operationId: get_team_billing_plan responses: '200': content: application/json: example: active: true guestLimit: 3 guests: 1 plan: pro seats: 3 upgradeUrl: https://billing.hanzo.ai schema: $ref: '#/components/schemas/team.planInfo' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the plan and seat counts for the caller's OWN org, resolved from the VERIFIED team session token \u2014\ \ never a client header." tags: - team x-app: team x-public: true x-tool: true /v1/team/billing/ui: get: description: "Serves the usage-and-wallet page the Team front links to \u2014 HTML, not JSON. It is a static React build\ \ compiled into this binary, so there is no upstream to be down and no build step at request time.\n\nSESSION-GATED:\ \ without a verified team session token \u2014 bearer, else the HttpOnly account cookie \u2014 the caller gets 401\ \ and not one byte of the page, so an anonymous browser meets a refusal rather than a shell that then fails to load\ \ anything.\n\nThe page is markup only. It reads money SAME-ORIGIN from cloud's own balance and usage endpoints, which\ \ pin the org from the IAM cookie the team OAuth callback set \u2014 nothing under this path proxies a money read,\ \ so there is no second auth mechanism here to get wrong. A deployment whose page was never built answers 503 naming\ \ the missing bundle, never a blank 200." operationId: get_team_billing_ui responses: 2XX: content: text/html; charset=utf-8: schema: format: binary type: string description: Success summary: Open the wallet page tags: - team x-app: team x-public: true /v1/team/collaborator: get: description: "Upgrades to the hocuspocus WebSocket the Team editor syncs its Y.js documents over: binary frames of document\ \ name, message type and payload, with ONE socket multiplexing every document a tab has open. The server is a relay\ \ and an ordered update log, not a CRDT engine \u2014 it replays the log to each joining peer and broadcasts every\ \ update to the rest, which converges because Y.js updates are commutative and idempotent. There is no body; the response\ \ is a protocol upgrade.\n\nBOTH LANES SHARE ONE ROOT. The client derives them from one configured URL \u2014 this\ \ socket at its root, the markup-snapshot RPC one segment in \u2014 so pointing the editor at this service is one\ \ value, and the two lanes cannot drift apart.\n\nAUTH IS IN-BAND, PER DOCUMENT, NOT ON THE UPGRADE. The handshake\ \ gates only on browser Origin (403 outside the team surfaces; no Origin at all is admitted, which is what a non-browser\ \ sends), and then the first frame for a document must be an Auth message carrying the same session or space token\ \ every other team route verifies \u2014 a browser WebSocket cannot set an Authorization header, which is why the\ \ token rides inside the protocol. Anything else on an unauthenticated document is answered with one permission denial\ \ and nothing further.\n\nEvery document is authorized on its own: the document's space must be the token's space\ \ when the token pins one, and the caller must be a member of it. A mismatch, an unknown space and a non-member deny\ \ alike with \"document not found\". Rooms are keyed by org and space and the persisted log's key embeds both, so\ \ a foreign document id can neither join a room nor read a blob.\n\nThe server pings every twenty seconds and drops\ \ a socket silent for sixty, so a backgrounded tab \u2014 whose JS timers are throttled but whose network stack still\ \ auto-pongs \u2014 stays connected instead of dying into a reconnect loop." operationId: get_team_collaborator summary: Open the live collaborative-editing socket tags: - team x-app: team x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/team/collaborator/rpc/{documentId}: post: description: "CollabRPC is the collaborative-markup snapshot plane the Team front's editor\nspeaks: createContent stores\ \ a document field's markup at a fresh, immutable\nblob ref and returns it, updateContent stores a new snapshot and\ \ answers\nnothing, and getContent reads back the exact snapshot a ref names.\n\ncreateContent ALSO seeds the live-editing\ \ update log from the front-supplied\nY.js update, so a dialog-authored description is visible in the collaborative\n\ editor \u2014 which replays that log \u2014 and not only in snapshot reads.\nupdateContent never touches that log:\ \ peers may be live-editing the document,\nand their edits are not this call's to overwrite.\n\nEvery call is scoped\ \ to the caller's VERIFIED session or space token: the\ndocumentId's space must be the token's space when the token\ \ names one,\nand the caller must be a member of it. An unknown space, another tenant's\nspace and a space the caller\ \ is not in all answer the same 404, so a\nprobe learns nothing about what exists." operationId: post_team_collaborator_rpc_by_documentid parameters: - description: "DocumentID addresses the document field, as\n\"|||\" \u2014\ \ the\ncollaborator-client encodeDocumentId shape, from the path." example: "6579\u2026|tracker:class:Issue|issue-1|description" in: path name: documentId required: true schema: type: string requestBody: content: application/json: example: documentId: "6579\u2026|tracker:class:Issue|issue-1|description" method: getContent payload: source: issue-1-description-1730000000000 schema: $ref: '#/components/schemas/team.collabRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.collabResult' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'CollabRPC is the collaborative-markup snapshot plane the Team front''s editor speaks: createContent stores a document field''s markup at a fresh, immutable blob ref and returns it, updateContent stores a new snapshot and answers nothing, and getContent reads back the exact snapshot a ref names.' tags: - team x-app: team x-public: true x-tool: true /v1/team/dms: post: description: "Opens the direct message between the caller and the people named,\nor answers the one that already exists\ \ \u2014 there is one conversation per set of\npeople, however many times it is asked for.\n\nEvery person named must\ \ be a member of the space or one of the org's agents;\na direct message with an agent is a conversation it answers\ \ every message in,\nand a guest may not open one.\nAnswers 201 when this call opened it and 200 when it was already\ \ there." operationId: post_team_dms requestBody: content: application/json: example: members: - "9f2c\u2026" schema: $ref: '#/components/schemas/team.teamDirectOpen' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamDirect' description: ok '201': content: application/json: schema: $ref: '#/components/schemas/team.teamDirect' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Opens the direct message between the caller and the people named, or answers the one that already exists \u2014\ \ there is one conversation per set of people, however many times it is asked for." tags: - team x-app: team x-public: true x-tool: true /v1/team/docs: get: description: 'Returns the documents of a space the caller may see, with the teamspaces they are grouped in. These are the Team client''s own document rows, so a page created there is here with no sync. Each carries the `collaborator` id its body opens with on the /v1/team/collaborator socket. A private teamspace''s documents are listed only for its members.' operationId: get_team_docs parameters: - description: Space is the space uuid. Optional for a caller in exactly one space. in: query name: space required: false schema: type: string - description: Teamspace narrows the answer to one teamspace. in: query name: teamspace required: false schema: type: string - description: Parent narrows the answer to one document's children. in: query name: parent required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamDocs' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the documents of a space the caller may see, with the teamspaces they are grouped in. tags: - team x-app: team x-public: true x-tool: true post: description: "Creates a document, as the caller, after its siblings.\n\nIt is created through the Team client's own\ \ write path, so it appears in an\nopen Documents sidebar live, and its author is subscribed to it: a comment on\n\ it lands in their inbox. Its body starts empty \u2014 open the returned\n`collaborator` id on the /v1/team/collaborator\ \ socket to write it.\n\nA space whose caller can write no teamspace at all gets one on its first\ndocument: a public\ \ \"General\" teamspace every current member of the space is\nin, which is what the Team client would otherwise make\ \ somebody create by hand\nbefore the first page. Guests and agents are not made members of it." operationId: post_team_docs requestBody: content: application/json: example: parent: d41c title: Runbook schema: $ref: '#/components/schemas/team.teamDocNew' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/team.teamDoc' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a document, as the caller, after its siblings. tags: - team x-app: team x-public: true x-tool: true /v1/team/docs/{id}: delete: description: "Removes a document with everything nested under it and every\ncomment on any of them \u2014 the Team client's\ \ own delete, which takes the\nsubtree with it. Its author, an owner of its teamspace or an admin of the\nspace may\ \ delete it. Answers 204.\n\nThe document model has no archived state for a page (only a teamspace can be\narchived),\ \ so there is nothing softer to offer here than removal." operationId: delete_team_docs_by_id parameters: - description: ID is the document, from the path. in: path name: id required: true schema: type: string - description: Space is the space uuid holding it. example: "0e3c\u2026" in: query name: space required: false schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Removes a document with everything nested under it and every comment on any of them \u2014 the Team client's\ \ own delete, which takes the subtree with it." tags: - team x-app: team x-public: true x-tool: true get: description: Returns one document the caller may see. operationId: get_team_docs_by_id parameters: - description: ID is the document, from the path. example: d41c in: path name: id required: true schema: type: string - description: Space is the space uuid holding it. example: "0e3c\u2026" in: query name: space required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamDoc' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one document the caller may see. tags: - team x-app: team x-public: true x-tool: true patch: description: "Renames a document or moves it \u2014 under another document, to the top\nof its teamspace, or to another\ \ teamspace with everything nested under it.\n\nAnyone who can write the teamspace may rename or move a page, as in\ \ the Team\nclient. A move goes after its new siblings, and a document cannot be moved\nunder itself or under one\ \ of its own descendants." operationId: patch_team_docs_by_id parameters: - description: ID is the document, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: parent: '' space: "0e3c\u2026" title: Runbook (2026) schema: $ref: '#/components/schemas/team.teamDocEdit' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamDoc' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Renames a document or moves it \u2014 under another document, to the top of its teamspace, or to another teamspace\ \ with everything nested under it." tags: - team x-app: team x-public: true x-tool: true /v1/team/docs/{id}/comments: get: description: "Returns the comments on a document, oldest first \u2014 the same\nmessage shape a room's conversation\ \ answers." operationId: get_team_docs_by_id_comments parameters: - description: ID is the document, from the path. in: path name: id required: true schema: type: string - description: Space is the space uuid holding it. in: query name: space required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamMessages' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the comments on a document, oldest first \u2014 the same message shape a room's conversation answers." tags: - team x-app: team x-public: true x-tool: true post: description: 'Comments on a document, as the caller. It is a message attached to the document, written through the Team client''s own path: the people subscribed to the document and anyone mentioned are notified, and an agent mentioned answers with a comment of its own. It can be replied to, reacted to, edited and deleted with the message ops.' operationId: post_team_docs_by_id_comments parameters: - description: ID is the document, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: space: "0e3c\u2026" text: "step 3 is out of date, <@9f2c\u2026>" schema: $ref: '#/components/schemas/team.teamCommentWrite' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/team.teamMessage' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Comments on a document, as the caller. tags: - team x-app: team x-public: true x-tool: true /v1/team/events: get: description: "Holds one text/event-stream open per caller and pushes each change as it is written \u2014 by the Team\ \ client, a typed op or an agent \u2014 filtered to the rooms, documents and inbox the caller may see under the same\ \ rule the ops apply. Authenticate with `Authorization: Bearer` (a fetch-based SSE reader; a browser EventSource cannot\ \ set it). `?space=` narrows the stream to one space; without it the stream covers every space the caller is a member\ \ of at connect.\n\nEach frame is `event: ` and a JSON `data:` line carrying `space` and one of:\n- `message.created`,\ \ `message.updated` \u2014 `message`, the message as the message ops answer it (a room message, a thread reply or\ \ a document comment; a count or reaction change is an update);\n- `message.deleted` \u2014 `id`, and `room`/`doc`/`thread`\ \ where known;\n- `reaction.changed` \u2014 `message`, with its reactions as they now stand;\n- `room.changed` \u2014\ \ `room`, as the room listing answers it, sent to those who can see it after the change (somebody removed from a private\ \ room is not told; the room list is the truth);\n- `doc.changed` \u2014 `doc`, as the document ops answer it (`removed:\ \ true` and `id` when deleted); body edits happen on the collaborator socket and are not streamed here;\n- `inbox.created`,\ \ `inbox.updated` \u2014 `item`, one of the caller's own notifications.\n\nA `: ping` comment is written every 25\ \ seconds. There is no resume: the server keeps no event sequence, so frames carry no `id` and Last-Event-ID is ignored;\ \ on reconnect, re-read the ops. A stream that falls too far behind is closed rather than allowed to slow a writer,\ \ and so is one whose caller is no longer a member of the space an event is in. 401 without a verified team credential;\ \ 404 for a named space the caller is not in." operationId: get_team_events responses: 2XX: content: text/event-stream: schema: format: binary type: string description: Success summary: Stream live changes to what the caller may see, as Server-Sent Events tags: - team x-app: team x-public: true /v1/team/files/{space}: post: description: "Stores one file in a space's blob store and answers the blob id it is addressable by, as plain text \u2014\ \ the front discards that body, it is there for a caller driving this by hand.\n\nThe body is a multipart form with\ \ a `file` part, and THAT PART'S FILENAME IS THE BLOB ID: the client mints it (a uuid v4) and the server stores under\ \ it, so a part whose filename is not a uuid is refused rather than assigned one. A file over 100 MiB is 413 and an\ \ empty one is 400.\n\nThe caller must hold a verified session or space token AND be a member of the space; an unknown\ \ space, another tenant's space and a space the caller is not in all answer the same 404, so a probe learns nothing\ \ about what exists. The stored key embeds the verified org and the space, so an upload cannot land in another tenant's\ \ box whatever id it names. A storage backend that is unavailable fails closed with 502 rather than reporting a write\ \ it never made." operationId: post_team_files_by_space parameters: - in: path name: space required: true schema: type: string requestBody: content: application/octet-stream: schema: format: binary type: string responses: 2XX: content: text/plain; charset=utf-8: schema: format: binary type: string description: Success summary: Upload a file into a space tags: - team x-app: team x-public: true /v1/team/files/{space}/{filename}: delete: description: "Removes one blob from a space's file store. The caller must\nhold a verified session AND be a member of\ \ the space; anything else \u2014 an\nunknown space, another tenant's space, a space the caller is not\nin \u2014\ \ answers the same 404, so a probe learns nothing about what exists.\n\nIt is IDEMPOTENT: deleting a present or an\ \ absent blob both answer 204, so a\ndelete never confirms a blob's existence and a foreign blob id (a physical key\n\ the caller can never name into another tenant's box) is a harmless no-op. A\nstorage backend that is unavailable fails\ \ closed with 502 rather than lying\nabout success." operationId: delete_team_files_by_space_by_filename parameters: - description: Space is the space uuid the blob belongs to, from the path. example: "6579\u2026" in: path name: space required: true schema: type: string - description: 'Filename is the last path segment, which the front sets to the blob id when it sends no explicit `file`.' in: path name: filename required: true schema: type: string - description: File is the blob id, and wins over the path segment when both are present. example: "0d4f\u2026" in: query name: file required: false schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one blob from a space's file store. tags: - team x-app: team x-public: true x-tool: true get: description: "Streams one blob's raw BYTES back \u2014 this is the read side of the space file store, not a JSON envelope\ \ around it.\n\nTHE BLOB IS NAMED BY THE `file` QUERY PARAMETER, NOT BY :filename. The path segment is only the name\ \ a browser saves the download under; a request without ?file= is a 400 no matter what the path says.\n\nThe Content-Type\ \ is derived from the STORED BYTES, never from the name: only png, jpeg, gif and webp, recognized by their magic bytes,\ \ are served inline under their true type, and everything else is served inert as application/octet-stream with an\ \ attachment disposition. Every response carries nosniff, so a file uploaded under an .svg or .html name cannot be\ \ talked into executing in a viewer's origin. Blobs are immutable, so a hit caches privately for a year.\n\nSame gate\ \ as the upload: verified token, membership of the space. A genuine miss, another tenant's space, a space the caller\ \ is not in, and a blob id belonging to a different space are ONE answer \u2014 404 \u2014 because the physical key\ \ is org- and space-scoped and a foreign id is simply a key that does not exist. An unavailable backend is a 502,\ \ never an empty 200." operationId: get_team_files_by_space_by_filename parameters: - in: path name: space required: true schema: type: string - in: path name: filename required: true schema: type: string responses: 2XX: content: application/octet-stream: schema: format: binary type: string description: Success summary: Download a space file tags: - team x-app: team x-public: true /v1/team/inbox: get: description: 'Returns the caller''s notifications, newest first, each with the room or document it is about and the message that caused it. These are the rows the Team client''s Inbox reads, so a notification cleared there is cleared here. Only the caller''s own notifications are ever listed.' operationId: get_team_inbox parameters: - description: Space is the space uuid. Optional for a caller in exactly one space. in: query name: space required: false schema: type: string - description: Archived lists the archived notifications instead of the live ones. in: query name: archived required: false schema: type: boolean responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamInbox' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the caller's notifications, newest first, each with the room or document it is about and the message that caused it. tags: - team x-app: team x-public: true x-tool: true /v1/team/inbox/read: post: description: 'Marks every live notification of the caller''s read, and says how many it changed.' operationId: post_team_inbox_read requestBody: content: application/json: example: space: "0e3c\u2026" schema: $ref: '#/components/schemas/team.teamInboxAll' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamInboxCleared' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Marks every live notification of the caller's read, and says how many it changed. tags: - team x-app: team x-public: true x-tool: true /v1/team/inbox/{id}/archive: post: description: "Archives one of the caller's notifications \u2014 read, and\nout of the live inbox \u2014 and answers\ \ it. It is listed again with\n`archived=true`." operationId: post_team_inbox_by_id_archive parameters: - description: ID is the notification, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: space: "0e3c\u2026" schema: $ref: '#/components/schemas/team.teamInboxAt' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamInboxItem' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Archives one of the caller's notifications \u2014 read, and out of the live inbox \u2014 and answers it." tags: - team x-app: team x-public: true x-tool: true /v1/team/inbox/{id}/read: post: description: 'Marks one of the caller''s notifications read and answers it. Another person''s notification is a 404, as one that does not exist is.' operationId: post_team_inbox_by_id_read parameters: - description: ID is the notification, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: space: "0e3c\u2026" schema: $ref: '#/components/schemas/team.teamInboxAt' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamInboxItem' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Marks one of the caller's notifications read and answers it. tags: - team x-app: team x-public: true x-tool: true /v1/team/members: get: description: 'Returns the people and agents in a space, with the name, avatar, role and presence a client draws them with. People come from IAM, the membership authority; agents from the org''s agent registry; avatars from the Person documents the Team client edits. An IAM that does not answer is a 502 rather than an empty roster, and an agent registry that does not answer is named in `degraded` beside the people who did load. The caller must be a member of the space.' operationId: get_team_members parameters: - description: Space is the space uuid. Optional for a caller in exactly one space. in: query name: space required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamMembers' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the people and agents in a space, with the name, avatar, role and presence a client draws them with. tags: - team x-app: team x-public: true x-tool: true /v1/team/messages/{id}: delete: description: 'Removes a message, with its replies, reactions, files and the inbox notifications that point at it. Its author may delete it, and so may an owner or admin of the space. Each removal is a platform remove through the Team client''s write path, so the message disappears from every open client live. Answers 204.' operationId: delete_team_messages_by_id parameters: - description: ID is the message, from the path. in: path name: id required: true schema: type: string - description: 'Space names the space holding it. A message id is unique within a space, not across the org.' example: "0e3c\u2026" in: query name: space required: false schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes a message, with its replies, reactions, files and the inbox notifications that point at it. tags: - team x-app: team x-public: true x-tool: true patch: description: 'Rewrites what a message says. Only its author may edit it. The edit is an ordinary update of the message document through the Team client''s own write path, stamped with editedOn, so an open client shows the new text and the "edited" mark live. Somebody the new text mentions for the first time is notified.' operationId: patch_team_messages_by_id parameters: - description: ID is the message, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: space: "0e3c\u2026" text: deploying at 3pm instead schema: $ref: '#/components/schemas/team.teamMessageEdit' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamMessage' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Rewrites what a message says. tags: - team x-app: team x-public: true x-tool: true /v1/team/messages/{id}/reactions/{emoji}: delete: description: 'Takes back the caller''s reaction to a message and answers the message with its reactions as they now stand. Taking back a reaction the caller never made changes nothing and is not an error.' operationId: delete_team_messages_by_id_reactions_by_emoji parameters: - description: ID is the message, from the path. in: path name: id required: true schema: type: string - description: Emoji is the reaction, from the path (percent-encoded on the wire). in: path name: emoji required: true schema: type: string - description: Space names the space holding the message. example: "0e3c\u2026" in: query name: space required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamMessage' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Takes back the caller's reaction to a message and answers the message with its reactions as they now stand. tags: - team x-app: team x-public: true x-tool: true put: description: "Adds the caller's reaction to a message and answers the message with\nits reactions as they now stand.\ \ Reacting twice with one emoji is one\nreaction: the second call changes nothing.\n\nThe emoji is the last path segment,\ \ percent-encoded \u2014 PUT\n/v1/team/messages/7a1c/reactions/%F0%9F%91%8D for \U0001F44D." operationId: put_team_messages_by_id_reactions_by_emoji parameters: - description: ID is the message, from the path. in: path name: id required: true schema: type: string - description: Emoji is the reaction, from the path (percent-encoded on the wire). in: path name: emoji required: true schema: type: string requestBody: content: application/json: example: space: "0e3c\u2026" schema: $ref: '#/components/schemas/team.teamReactionWrite' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamMessage' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Adds the caller's reaction to a message and answers the message with its reactions as they now stand. tags: - team x-app: team x-public: true x-tool: true /v1/team/messages/{id}/replies: get: description: 'Returns a message''s thread, oldest first, each reply with its reactions and files.' operationId: get_team_messages_by_id_replies parameters: - description: ID is the message, from the path. in: path name: id required: true schema: type: string - description: 'Space names the space holding it. A message id is unique within a space, not across the org.' in: query name: space required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamMessages' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns a message's thread, oldest first, each reply with its reactions and files. tags: - team x-app: team x-public: true x-tool: true post: description: "Answers a message in its thread, as the caller.\n\nA reply is a thread message attached to the one it\ \ answers, written through\nthe Team client's own path: the parent's reply count and last-reply time move\nwith it,\ \ the people already in the thread and anyone mentioned are notified,\nand an agent mentioned in a reply answers in\ \ the same thread. A reply cannot\nitself be replied to \u2014 threads are one level deep, as in the Team client." operationId: post_team_messages_by_id_replies parameters: - description: ID is the message being answered, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: space: "0e3c\u2026" text: on it schema: $ref: '#/components/schemas/team.teamReplyWrite' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/team.teamMessage' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Answers a message in its thread, as the caller. tags: - team x-app: team x-public: true x-tool: true /v1/team/public: get: description: "Lists the rooms orgs have published, across every org.\n\nIt is NOT part of GET /rooms, and the separation\ \ is the point: that address\nanswers the CALLER'S rooms, so folding these in would put strangers' channels\nin somebody's\ \ own sidebar.\n\nIt reads the directory and never a tenant's store. Every field it can answer\nwith is one an org\ \ published by making a room public, so there is nothing here\nto scope by org \u2014 a directory only its own org\ \ can read is not a directory.\nAn authenticated principal is still required, because an anonymous crawler is\nnot\ \ who this is for." operationId: get_team_public parameters: - description: Q matches a room's name or its topic. in: query name: q required: false schema: type: string - description: Org narrows to one org's published rooms. in: query name: org required: false schema: type: string - description: "Limit caps the page, 50 when unstated and 200 at most. An unparseable\nvalue reads as unstated rather\ \ than as zero \u2014 zero pages is not an answer\nanybody asked for." in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.publicRooms' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the rooms orgs have published, across every org. tags: - team x-app: team x-public: true x-tool: true /v1/team/rooms: get: description: 'Returns the rooms the caller may see, with the kind and work facet each carries. A signed-in TEAM MEMBER reads every room of every space they are in that is open to them: public channels, and the private channels and direct messages that name them. An APPLICATION of the org (its own machine credential) reads the public channels of the org''s spaces and nothing that belongs to particular people. Anybody else without a team session is 401. It reads the SAME Chunter documents the transactor serves, so a room opened in the Team client appears here with no sync, and a facet written here is read by anything holding the document.' operationId: get_team_rooms responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamRooms' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the rooms the caller may see, with the kind and work facet each carries. tags: - team x-app: team x-public: true x-tool: true post: description: "Opens a named room and answers it as the store now holds it.\n\nIt writes through the SAME applyTx path\ \ the Team client uses, so a room opened\nhere is broadcast to every live client of the space and appears in an open\n\ sidebar without a reload \u2014 the same property listRooms rests on, read from the\nwrite side.\n\nA SIGNED-IN TEAM\ \ MEMBER opens it as themselves: they are its first member and\nits owner, every other member named must be somebody\ \ the space knows (a person\nin it, or one of the org's agents), and a guest may not open rooms at all. An\napplication\ \ of the org opens it as the org, with exactly the members it names.\nAnybody else without a team session is 401.\n\ \nTWO TRANSACTIONS, NOT ONE, when the request states a facet. The document and\nits mixin are separate writes in this\ \ model (bindRoom writes only the second),\nand composing them here rather than inventing a combined tx keeps one\ \ write\npath for each. A create that lands and a facet that does not is visible as a\nroom with default intent, which\ \ is the honest partial state." operationId: post_team_rooms requestBody: content: application/json: example: bindings: - issue:1010 life: bound name: bugfix-1010 schema: $ref: '#/components/schemas/team.teamRoomNew' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/team.teamRoom' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Opens a named room and answers it as the store now holds it. tags: - team x-app: team x-public: true x-tool: true /v1/team/rooms/{id}: patch: description: "Renames a channel, sets its topic, or archives or reopens it, and\nanswers the room as it now stands.\n\ \nIts owners and the space's admins may edit a room; a room nobody owns \u2014 one\nopened by an integration \u2014\ \ may be edited by anyone in it, and a direct\nmessage by either person in it. Archiving withdraws a public channel\ \ from the\ncross-org directory in the same write; reopening lists it again." operationId: patch_team_rooms_by_id parameters: - description: ID is the room, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: archived: true space: "0e3c\u2026" schema: $ref: '#/components/schemas/team.teamRoomEdit' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamRoom' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Renames a channel, sets its topic, or archives or reopens it, and answers the room as it now stands. tags: - team x-app: team x-public: true x-tool: true put: description: "States what a room is for: its lifecycle intent, and what it is\nabout. It answers the room as it now\ \ stands.\n\nThe write is a platform MIXIN on the room document, applied through the\nSAME applyTx path the Team client's\ \ own writes take and broadcast to every\nconnected client \u2014 so a room bound here updates live in an open space\n\ rather than on the next reload. A team member binds a room they can see; an\napplication of the org binds its public\ \ channels only; anybody else without\na team session is 401." operationId: put_team_rooms_by_id parameters: - description: 'ID is the room to bind, from the path. The URL is the authority; a body carrying another id cannot redirect the write.' in: path name: id required: true schema: type: string requestBody: content: application/json: example: bindings: - repo:hanzoai/cloud - issue:1010 life: bound space: "0e3c\u2026" schema: $ref: '#/components/schemas/team.teamRoomBind' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamRoom' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'States what a room is for: its lifecycle intent, and what it is about.' tags: - team x-app: team x-public: true x-tool: true /v1/team/rooms/{id}/members: get: description: 'Returns the people and agents in one room, as the roster describes them. The caller must be able to see the room.' operationId: get_team_rooms_by_id_members parameters: - description: ID is the room, from the path. The URL is the authority. in: path name: id required: true schema: type: string - description: 'Space names the space holding the room, and is required for the reason the bind op requires it: a room id is unique within a space and not across the org, so searching every space for a match would make the answer depend on iteration order.' in: query name: space required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamRoomMembers' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the people and agents in one room, as the roster describes them. tags: - team x-app: team x-public: true x-tool: true post: description: "Adds people to a room and answers the room as it now stands.\n\nAnyone in the space except a guest may\ \ join a public channel by naming\nthemselves. Adding somebody else takes being in the room already (or\nadministering\ \ the space, for a room they can see). A direct message's people\nare what it is, so none can be added \u2014 open\ \ another one. Everyone added must\nbe a member of the space or one of the org's agents; an agent added to a room\n\ answers when it is @-mentioned there." operationId: post_team_rooms_by_id_members parameters: - description: ID is the room, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: members: - "9f2c\u2026" space: "0e3c\u2026" schema: $ref: '#/components/schemas/team.teamRoomJoin' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamRoom' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Adds people to a room and answers the room as it now stands. tags: - team x-app: team x-public: true x-tool: true /v1/team/rooms/{id}/members/{account}: delete: description: "Takes one person out of a room \u2014 the caller leaving, when the\naccount is their own. Removing somebody\ \ else takes owning the room or\nadministering the space. Leaving a room you are not in, or removing somebody\nwho\ \ is not there, changes nothing. A direct message cannot be left: its people\nare what it is. Answers 204." operationId: delete_team_rooms_by_id_members_by_account parameters: - description: ID is the room, from the path. in: path name: id required: true schema: type: string - description: Account is the account uuid to remove, from the path. Your own is leaving. in: path name: account required: true schema: type: string - description: Space is the space uuid holding the room. example: "0e3c\u2026" in: query name: space required: false schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Takes one person out of a room \u2014 the caller leaving, when the account is their own." tags: - team x-app: team x-public: true x-tool: true /v1/team/rooms/{id}/messages: get: description: 'Returns the tail of one room''s conversation, oldest first, each message with its reactions, files and thread count. It reads the SAME Chunter documents the transactor serves, so a message typed in the Team client is here with no sync. A team member reads any room they may see; an application of the org reads public channels only; anybody else without a team session is 401. A room the caller may not read answers 404 rather than 403, so a probe learns nothing about what exists.' operationId: get_team_rooms_by_id_messages parameters: - description: ID is the room, from the path. The URL is the authority. in: path name: id required: true schema: type: string - description: 'Space names the space holding the room, and is required for the reason the bind op requires it: a room id is unique within a space and not across the org, so searching every space for a match would make the answer depend on iteration order.' in: query name: space required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.teamMessages' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the tail of one room's conversation, oldest first, each message with its reactions, files and thread count. tags: - team x-app: team x-public: true x-tool: true post: description: "Says one thing in a room, as the caller.\n\nThe write goes through the SAME applyTx path the Team client's\ \ own messages\ntake and is broadcast to every connected client of the space, so a message\nsent here appears live\ \ in an open room rather than on the next reload. It\nanswers the message as the store now HOLDS it.\n\n`<@account-uuid>`\ \ in the text is stored as the platform's mention, so the\nperson is notified in their inbox. Mentioning one of the\ \ org's agents \u2014 or\nwriting in a direct message with one \u2014 wakes it: it runs as itself, on behalf\nof the\ \ caller, and posts its answer into the same room. The caller must be\nable to see the room, and an archived room\ \ refuses new messages (409)." operationId: post_team_rooms_by_id_messages parameters: - description: ID is the room to say it in, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: space: "0e3c\u2026" text: "deploying now, <@9f2c\u2026> can you watch the dashboards?" schema: $ref: '#/components/schemas/team.teamMessageWrite' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/team.teamMessage' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Says one thing in a room, as the caller. tags: - team x-app: team x-public: true x-tool: true /v1/team/transactor/statistics: get: description: "Returns the transactor's live sessions for the space the caller's\ncredential names \u2014 the endpoint\ \ the front's space switcher and server panel\npoll on the transactor base. `token` carries the same two lanes the\ \ socket's path\nsegment does: a space UUID names the space and is authorized against the\nmembership rows, an HS256\ \ space token names it in its signed claims.\nactiveSessions carries ONLY that one space, never another tenant's sessions.\n\ An unverifiable credential, or one the caller is no member under, is 401." operationId: get_team_transactor_statistics parameters: - description: Token is the space token minted by selectWorkspace. example: "eyJhbGciOiJIUzI1NiJ9\u2026" in: query name: token required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/team.statsOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the transactor's live sessions for the space the caller's credential names \u2014 the endpoint the\ \ front's space switcher and server panel poll on the transactor base." tags: - team x-app: team x-public: true x-tool: true /v1/team/transactor/{token}: get: description: "Upgrades to the WebSocket the Team client runs an entire space over: every frame is a ZAP envelope wrapping\ \ one JSON-RPC message \u2014 findAll/findOne reads against the space's documents, tx writes that broadcast to the\ \ other live sessions, hello negotiating JSON rather than msgpack. The response is a protocol upgrade, so there is\ \ no body to read.\n\nTHE PATH SEGMENT IS THE CREDENTIAL. It is the space token selectWorkspace minted \u2014 bearer-equivalent,\ \ and sitting in a URL that proxies and access logs record, which is exactly why it expires in twelve hours and is\ \ re-minted on demand rather than being long-lived like the session token. It is decoded and verified (signature and\ \ expiry) BEFORE the upgrade, so a bad one is a 401 and never a socket that is accepted and then dropped, and it must\ \ carry both an account and a space claim. Nothing ambient authorizes this socket: a WebSocket is exempt from CORS,\ \ so a cookie-borne credential would make the Origin check the only access control on the whole data plane.\n\nThe\ \ tenant is the token's SIGNED org claim and it keys every store path, so no header can name another space's data.\ \ The upgrade ALSO refuses a browser Origin outside the team surfaces with 403 \u2014 otherwise any page could open\ \ an authenticated socket with a token it lured out of a logged-in browser \u2014 while a request with no Origin at\ \ all is admitted, because that is what a non-browser client sends.\n\nOn connect the space's system spaces are seeded\ \ once and the roster is reconciled every time, so the org's human members and its bots are present as space people\ \ without a separate sync call." operationId: get_team_transactor_by_token parameters: - in: path name: token required: true schema: type: string summary: Open the space data-plane socket tags: - team x-app: team x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/tel/calls: get: description: 'Lists the calls this org has placed or received, newest first. Like the message list beside it, these are our own records rather than the carrier''s.' operationId: get_tel_calls responses: '200': content: application/json: schema: $ref: '#/components/schemas/tel.callList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the calls this org has placed or received, newest first. tags: - tel x-app: tel x-public: true x-tool: true post: description: 'Dials. An `agent` names a Hanzo assistant to answer it; the call is refused up front when no assistant plane is configured, because a call that connects to silence has already cost the person who answered it.' operationId: post_tel_calls requestBody: content: application/json: schema: $ref: '#/components/schemas/tel.callInput' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/tel.Call' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Dials. tags: - tel x-app: tel x-public: true x-tool: true /v1/tel/calls/{id}: delete: description: 'Ends a call this org placed. The holding is read for THIS org before the carrier is asked, for the reason releaseNumber gives one surface up: an id belonging to another tenant would otherwise be hung up by whoever guessed it.' operationId: delete_tel_calls_by_id parameters: - in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Ends a call this org placed. tags: - tel x-app: tel x-public: true x-tool: true /v1/tel/messages: get: description: "Lists the messages this org has sent or received, newest first. Records from\nour own store, not the carrier's\ \ \u2014 so it is what this platform did on the\norg's behalf, which is the set an audit or a bill has to agree with." operationId: get_tel_messages responses: '200': content: application/json: schema: $ref: '#/components/schemas/tel.messageList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the messages this org has sent or received, newest first. tags: - tel x-app: tel x-public: true x-tool: true post: description: "Sends a message from one of this org's own numbers.\n\n`from` must be a number the org HOLDS, checked\ \ against the store rather than\ntaken on trust \u2014 a caller that could send from any number could impersonate\n\ one, and the carrier would deliver it. `to` is required, and the body needs\ntext or media, because a message with\ \ neither is delivered as nothing and\nbilled as something." operationId: post_tel_messages requestBody: content: application/json: schema: $ref: '#/components/schemas/tel.messageInput' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/tel.SMS' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Sends a message from one of this org's own numbers. tags: - tel x-app: tel x-public: true x-tool: true /v1/tel/numbers: get: description: "Lists the phone numbers this org HOLDS \u2014 the ones it has bought and not\nreleased. Distinct from\ \ the availability search one path down\n(`/numbers/available`), which asks the carrier what could be bought: this\n\ answers only from our own store, so it is what an org owns rather than what\nit could own." operationId: get_tel_numbers responses: '200': content: application/json: schema: $ref: '#/components/schemas/tel.numberList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the phone numbers this org HOLDS \u2014 the ones it has bought and not released." tags: - tel x-app: tel x-public: true x-tool: true post: description: 'Provisions with the carrier FIRST and records second. The other order records a holding that may not exist, and a number the platform believes it owns but cannot use is worse than one it failed to buy.' operationId: post_tel_numbers requestBody: content: application/json: schema: $ref: '#/components/schemas/tel.buyInput' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/tel.Number' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Provisions with the carrier FIRST and records second. tags: - tel x-app: tel x-public: true x-tool: true /v1/tel/numbers/available: get: description: "Asks the carrier what is available to buy. Nothing is recorded \u2014\na search is not a holding, and\ \ treating it as one is how inventory leaks." operationId: get_tel_numbers_available parameters: - in: query name: Country required: false schema: type: string - in: query name: Area required: false schema: type: string - in: query name: Type required: false schema: type: string - in: query name: Limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/tel.numberList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Asks the carrier what is available to buy. tags: - tel x-app: tel x-public: true x-tool: true /v1/tel/numbers/{id}: delete: description: 'Checks the holding is THIS org''s before it reaches the carrier. Without that read, an id belonging to another tenant would be released by whoever guessed it.' operationId: delete_tel_numbers_by_id parameters: - in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Checks the holding is THIS org's before it reaches the carrier. tags: - tel x-app: tel x-public: true x-tool: true /v1/tel/summary: get: description: 'Counts what this org holds on the telephony plane: its numbers, its calls and its messages. The one read a dashboard makes before it asks for any list, so it answers three totals and no rows.' operationId: get_tel_summary responses: '200': content: application/json: schema: $ref: '#/components/schemas/tel.summary' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Counts what this org holds on the telephony plane: its numbers, its calls and its messages.' tags: - tel x-app: tel x-public: true x-tool: true /v1/template: get: description: 'Lists the public starter-kit catalog plus, for a validated caller, that org''s own private kits. No request field can widen the scope: the org comes from the validated principal, so an anonymous or cross-org caller structurally sees the public catalog only.' operationId: get_template responses: '200': content: application/json: schema: $ref: '#/components/schemas/template.kitList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the public starter-kit catalog plus, for a validated caller, that org's own private kits. tags: - template x-app: template x-public: true x-tool: true post: description: 'Creates a starter kit PRIVATE to the caller''s org and answers 201 with the stored kit. The owner is stamped by the server, so a body "org" is never trusted; publishing over a public-catalog slug is 409, so a slug still names exactly one kit.' operationId: post_template requestBody: content: application/json: example: framework: Next.js 14 slug: acme-portal title: Acme Internal Portal schema: $ref: '#/components/schemas/template.publishKitIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/template.StarterKit' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Creates a starter kit PRIVATE to the caller's org and answers 201 with the stored kit. tags: - template x-app: template x-public: true x-tool: true /v1/template/{slug}: delete: description: 'Deletes the caller org''s OWN starter kit. A slug they do not own is a 404, never a delete: the DELETE binds org.' operationId: delete_template_by_slug parameters: - description: Slug is the starter kit to act on, from the path. example: acme-portal in: path name: slug required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deletes the caller org's OWN starter kit. tags: - template x-app: template x-public: true x-tool: true get: description: 'Returns one starter kit: the caller org''s own by that slug, else the public catalog''s. A slug another org owns reads as not found.' operationId: get_template_by_slug parameters: - description: Slug is the starter kit to act on, from the path. example: folio in: path name: slug required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/template.StarterKit' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one starter kit: the caller org''s own by that slug, else the public catalog''s.' tags: - template x-app: template x-public: true x-tool: true put: description: 'Overwrites the caller org''s OWN starter kit at the path slug, answering the stored kit. A slug they do not own is 404, never a create: the UPDATE binds org, so a PUT can never reach another org''s kit.' operationId: put_template_by_slug parameters: - description: Slug is the kit to replace, from the path. example: acme-portal in: path name: slug required: true schema: type: string requestBody: content: application/json: example: slug: acme-portal title: Acme Internal Portal v2 schema: $ref: '#/components/schemas/template.replaceKitIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/template.StarterKit' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Overwrites the caller org's OWN starter kit at the path slug, answering the stored kit. tags: - template x-app: template x-public: true x-tool: true /v1/tool: get: description: "Lists every tool the caller's org and project can reach, from every\nsource, each flagged with whether\ \ it is activated. This is the discovery\nsurface: one flat set of names spanning connector actions, user functions,\n\ zap-service routes, agents, skills and the org's own external MCP servers,\ndeduplicated by name so the highest-precedence\ \ source wins a collision. It\nlists; it does not call \u2014 dispatch is POST /v1/tool/call." operationId: get_tool parameters: - description: "Source keeps only tools from one source \u2014 connector, function, zap-service,\nagent, skill or mcp.\ \ Empty keeps every source." in: query name: source required: false schema: type: string - description: 'Activated keeps only the tools activated for the caller''s org and project, and only when it is exactly the string "true".' in: query name: activated required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tool.toolList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists every tool the caller's org and project can reach, from every source, each flagged with whether it is activated. tags: - tool x-app: tool x-public: true x-tool: true /v1/tool/activation: get: description: "Reports which tools are switched on for the caller's org and\nproject. Activation is what makes a tool\ \ dispatchable and what makes it visible\nto an agent, so this is the set the MCP tool list is drawn from \u2014 every\ \ other\ntool in the registry is discoverable but refused at call time." operationId: get_tool_activation responses: '200': content: application/json: schema: $ref: '#/components/schemas/tool.activationSet' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports which tools are switched on for the caller's org and project. tags: - tool x-app: tool x-public: true x-tool: true put: description: "Switches tools on and off for the caller's org and project, and\nanswers with the resulting activated\ \ set. It is the ONE write path that turns\nskills, plugins and connectors into callable tools \u2014 an unactivated\ \ tool is\nlisted by discovery but refused 403 at dispatch. Activate is applied before\nDeactivate, so a name in both\ \ lists ends up off. More than 256 toggles in one\nrequest is refused 413.\n\nSwitching a skill (skill_) or\ \ a tool of an MCP server the org registered\n(_) changes what every agent run in the org carries, so\ \ it takes\nan admin of the org or a SuperAdmin: a member naming one is refused 403 and\nnothing in the request is\ \ switched. Every change is on the org's audit trail\nbefore it is made, and one the trail cannot record is refused\ \ 503." operationId: put_tool_activation requestBody: content: application/json: example: activate: - cloud_get_ping deactivate: [] schema: $ref: '#/components/schemas/tool.activationReq' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/tool.activationSet' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Switches tools on and off for the caller's org and project, and answers with the resulting activated set. tags: - tool x-app: tool x-public: true x-tool: true /v1/tool/call: post: description: "Runs one of the caller's activated tools and answers with its output.\n\nThis is the endpoint onto the\ \ tool plane's DYNAMIC half \u2014 the half no build-time\ncatalogue can hold, because it is per-tenant: an org's\ \ connected connector\nactions, its authored skills, its agents and functions, and the tools of every\nexternal MCP\ \ server it registered. A tool's existence, its price and its\nactivation are all rows, not code, so they cannot be\ \ known until the caller is.\n\nOne policy, the registry's: resolve by precedence, refuse an unactivated tool\n403,\ \ settle a priced one through the x402 client or fail closed 402, then\ndispatch to the winning source bound to the\ \ caller's own (org, project). One\nmetered unit, one audit record. A caller can only ever dispatch its own tools.\n\ \nDiscovery is GET /v1/tool \u2014 ?activated=true for the callable set." operationId: post_tool_call requestBody: content: application/json: example: arguments: channel: '#general' text: hi name: slack_post_message schema: $ref: '#/components/schemas/tool.toolCall' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/tool.toolResult' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Runs one of the caller's activated tools and answers with its output. tags: - tool x-app: tool x-public: true x-tool: true /v1/tool/catalog: get: description: "Lists the MCP servers the public registries publish, as we hold\nthem: our canonical copy of registry.modelcontextprotocol.io,\ \ plus what we\ndecided about each entry.\n\nThis is the SHELF an org picks from. A listing with a streamable-http\ \ endpoint\ncan be enabled as-is \u2014 POST /v1/tool/mcp/servers with its id \u2014 and its tools then\njoin the\ \ org's tool plane and the fleet's MCP server. A listing that only ships a\nstdio package needs a process to run it,\ \ which is why the transports are on\nevery entry rather than implied.\n\nHidden entries are absent: they are the\ \ ones we took off the shelf. A platform\nSuperAdmin sees them, because the same query answers \"what is on the shelf\"\ \ and\n\"what is in the catalog\" and two queries would drift apart.\n\nIt is PAGED \u2014 50 by default, 200 at most.\ \ The public registry publishes tens of\nthousands of servers, so an unbounded answer is a twenty-megabyte response\ \ and a\nstorefront that renders in a minute. total is the whole match, not the page.\n\nIt needs no credential: the\ \ shelf is a copy of public registries, the same for\nevery org, and a storefront shows it to a visitor before anyone\ \ signs in." operationId: get_tool_catalog parameters: - description: Q matches the name, title or description, case-insensitively. in: query name: q required: false schema: type: string - description: 'Featured keeps only the listings we put on the front of the shelf, and only when it is exactly the string "true".' in: query name: featured required: false schema: type: string - description: "Official keeps only the vendors' OWN servers \u2014 not third-party copies of\nthem \u2014 and only\ \ when it is exactly the string \"true\"." in: query name: official required: false schema: type: string - description: 'Limit bounds the page: default 50, maximum 200. A value that is not a positive integer reads as the default.' in: query name: limit required: false schema: format: int64 type: integer - description: Offset skips that many listings. in: query name: offset required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/tool.mcpCatalog' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused security: [] summary: 'Lists the MCP servers the public registries publish, as we hold them: our canonical copy of registry.modelcontextprotocol.io, plus what we decided about each entry.' tags: - tool x-app: tool x-public: true x-tool: true /v1/tool/catalog/sync: post: description: "Pulls the public MCP registry into our canonical copy and reports\nwhat changed. SuperAdmin only; every\ \ other caller is refused.\n\nIt is IDEMPOTENT: a listing is keyed by the publisher's own reverse-DNS name,\nso a\ \ second pass over an unchanged registry rewrites the same rows and reports\nadded=0, updated=0. It never deletes\ \ \u2014 a listing that vanishes upstream may be\none an org has already enabled, and dropping its description would\ \ not drop its\nserver. And it never touches CURATION: hidden, featured, an admin-set official\nand a logo survive\ \ every sync, because the write does not name those columns." operationId: post_tool_catalog_sync responses: '200': content: application/json: schema: $ref: '#/components/schemas/tool.mcpCatalogSync' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Pulls the public MCP registry into our canonical copy and reports what changed. tags: - tool x-app: tool x-public: true x-tool: true /v1/tool/catalog/{id}: get: description: "Returns one catalog entry in full: the publisher's description, its\nrepository and site, every package\ \ form with the runtime that launches it, and\nevery hosted endpoint. It is what a branding page renders, and what\ \ tells a\ncaller whether the listing can be enabled here and now (a streamable-http\nremote) or needs somewhere to\ \ run first (a stdio package).\n\nA HIDDEN listing is not served to an org \u2014 a shelf that renders what it does\n\ not list would be a way around the shelf \u2014 but is served to a SuperAdmin, who\nis the one deciding whether to\ \ put it back. Like the shelf, it needs no\ncredential." operationId: get_tool_catalog_by_id parameters: - description: "ID is the listing, from the path. It is the publisher's reverse-DNS name\nwith its one slash written\ \ as an underscore \u2014 \"com.stripe_mcp\"." in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tool.MCPListing' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused security: [] summary: 'Returns one catalog entry in full: the publisher''s description, its repository and site, every package form with the runtime that launches it, and every hosted endpoint.' tags: - tool x-app: tool x-public: true x-tool: true patch: description: "Sets what WE say about one catalog entry \u2014 hidden, featured,\nofficial, logo \u2014 and answers with\ \ the stored listing. SuperAdmin only; every\nother caller is refused.\n\nCuration is the half of a catalog row a\ \ sync cannot write, and this is the only\nthing that writes it. The upstream half is never editable here: a description\n\ that disagreed with the publisher's would be a fork of their listing, and the\nnext sync would silently undo it." operationId: patch_tool_catalog_by_id parameters: - description: ID is the listing to curate, from the path. in: path name: id required: true schema: type: string requestBody: content: application/json: example: featured: true official: false schema: $ref: '#/components/schemas/tool.curateReq' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/tool.MCPListing' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Sets what WE say about one catalog entry \u2014 hidden, featured, official, logo \u2014 and answers with the\ \ stored listing." tags: - tool x-app: tool x-public: true x-tool: true /v1/tool/mcp/servers: get: description: "Lists the external MCP servers the caller's org has registered.\nEach record carries the URL and the name\ \ of the header its credential is\ninjected into; the credential VALUE lives only in KMS and is never returned,\n\ so hasSecret is the whole of what this surface says about it. Each also says\nwhether its tools could be listed \u2014\ \ status, a reason when they could not, and\nhow many there are \u2014 so a server that contributes nothing says why.\n\ \nThe whole URL is answered to an admin of the org or a SuperAdmin, who register\nservers. A member is answered its\ \ scheme and host: which server it is, and not\na path or query an admin may have put a key in." operationId: get_tool_mcp_servers responses: '200': content: application/json: schema: $ref: '#/components/schemas/tool.mcpServerList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the external MCP servers the caller's org has registered. tags: - tool x-app: tool x-public: true x-tool: true post: description: "Gives the caller's org one more external MCP server, so its tools\njoin the org's tool plane and the fleet's\ \ MCP server. It is the ONE way an org\ngains a server, whether it typed the URL in or enabled a catalog listing:\ \ both\nwrite the SAME record, and `source` says which it was. A second registration\npath would be a second place\ \ for a server to exist, and then a second place to\nforget to check the credential.\n\nThe credential VALUE is sealed\ \ in KMS under a per-org ref; the row keeps only\nthe URL, the header name to inject it into, and a has-secret flag\ \ \u2014 so a secret\nwith no KMS configured is refused 503 rather than stored in the clear. The URL\nis SSRF-validated\ \ here and re-checked by the dialer at connect time, which is\nthe DNS-rebinding defense.\n\nEnabling a listing the\ \ org already enabled REVISES that server rather than\nadding a near-duplicate beside it, so a retried enable is the\ \ same one server.\nAnswers 201 with the stored record.\n\nA server and the tools of it the org activates are carried\ \ into every agent run\nin the org, so registering one \u2014 or revising one, credential included \u2014 takes\n\ an admin of the org or a SuperAdmin; a member is refused 403. The registration\nis on the org's audit trail before\ \ it is made, and one the trail cannot record\nis refused 503." operationId: post_tool_mcp_servers requestBody: content: application/json: example: authHeader: Authorization listing: com.stripe_mcp secret: "Bearer \u2026" schema: $ref: '#/components/schemas/tool.createServerReq' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/tool.MCPServer' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Gives the caller's org one more external MCP server, so its tools join the org's tool plane and the fleet's MCP server. tags: - tool x-app: tool x-public: true x-tool: true /v1/tool/mcp/servers/{id}: delete: description: 'Deregisters one of the caller org''s external MCP servers, so its tools leave the registry. Scoped to the caller''s org, so an id belonging to another tenant is a 404 and not a delete. Answers 204 with no body; a server this org does not have is 404. Like registering one it takes an admin of the org or a SuperAdmin, and is on the org''s audit trail before it is made.' operationId: delete_tool_mcp_servers_by_id parameters: - description: ID is the server to deregister, from the path. in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Deregisters one of the caller org's external MCP servers, so its tools leave the registry. tags: - tool x-app: tool x-public: true x-tool: true /v1/tool/plugins: get: description: "Reports what this deployment actually mounted: every subsystem the\ncomposition root declared and whether\ \ it is switched on. A plugin here is\nMOUNTED CODE that extends the deployment's own surface \u2014 not a tool an\ \ agent\ncalls \u2014 so this is an inventory and not a tool source. It is read off the same\nboot snapshot every\ \ traced request resolves its subsystem label against, so it\ncannot drift from what is serving. Enabled-only by default,\ \ because a caller\nasking what this deployment can do wants what is running; ?all=true adds the\nconfigured-but-off\ \ ones." operationId: get_tool_plugins parameters: - description: 'All includes the configured-but-disabled subsystems too, but only when it is exactly the string "true". Otherwise only the running ones are reported.' in: query name: all required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tool.pluginMountList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Reports what this deployment actually mounted: every subsystem the composition root declared and whether it is switched on.' tags: - tool x-app: tool x-public: true x-tool: true /v1/tool/plugins/authored: get: description: "Lists the plugins the caller's org BUILT, newest first,\neach with the TypeScript as authored. That is\ \ a different set with a different\nlifecycle from GET /v1/tool/plugins, which reports the subsystems this deployment\n\ mounted. The bundled CommonJS the runtime executes is never included, and\nneither is any credential \u2014 a plugin\ \ names the connectors provider it needs and\nreads the credential from ctx.auth at run time." operationId: get_tool_plugins_authored responses: '200': content: application/json: schema: $ref: '#/components/schemas/tool.authoredPluginList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the plugins the caller's org BUILT, newest first, each with the TypeScript as authored. tags: - tool x-app: tool x-public: true x-tool: true /v1/tool/plugins/authored/{id}: delete: description: 'Removes one of the caller org''s built plugins, so the runtime can no longer load it. Scoped to the caller''s org, so an id belonging to another tenant answers 404 and is not deleted.' operationId: delete_tool_plugins_authored_by_id parameters: - description: ID is the plugin to remove, from the path. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tool.pluginDeleted' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one of the caller org's built plugins, so the runtime can no longer load it. tags: - tool x-app: tool x-public: true x-tool: true /v1/tool/plugins/build: post: description: "Builds and stores one plugin for the caller's org. The 201 carries\nthe bundle's size, whether a model\ \ wrote the source, and the plugin as stored.\n\nPost `source` to build TypeScript as-is, or `spec` \u2014 an OpenAPI\ \ document or\nplain prose describing the endpoints \u2014 to have one generated; the generated\nsource comes back\ \ in the answer, so a caller reads what will run before it\nruns. Exactly one of the two, and `name` must be one lowercase\ \ path segment;\nboth or neither is 400.\n\nCOMPILING IS THE GATE. The source goes through the same pipeline the committed\n\ connectors do \u2014 esbuild to one CommonJS program, then compiled in the goja\nruntime that will actually execute\ \ it \u2014 and anything that fails is rejected and\nNEVER stored. So a plugin in the store is one this deployment\ \ has already\nloaded once, not one a model claimed was fine. A failed build answers 422\ncarrying the diagnostics\ \ a caller needs to fix it: the bundler's error\n(`detail`), the source that failed, and whether the model wrote it.\n\ \nCREDENTIALS ARE NOT PART OF A PLUGIN. A plugin names the connectors `provider`\nit needs and reads that credential\ \ from `ctx.auth` at run time, under KMS\ncustody. Source that carries something key-shaped is REFUSED rather than\n\ silently persisted \u2014 a scrubbed key looks like it worked." operationId: post_tool_plugins_build requestBody: content: application/json: example: name: acme provider: acme spec: POST /v1/things creates a thing schema: $ref: '#/components/schemas/tool.buildRequest' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/tool.buildOut' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Builds and stores one plugin for the caller's org. tags: - tool x-app: tool x-public: true x-tool: true /v1/tool/skills: get: description: "Lists the skills the caller's org can reach \u2014 the brand's embedded\ncatalogue plus the org's own\ \ authored ones \u2014 with each one's activation flag.\nA skill is discovery and activation metadata attached to\ \ an agent, never called\ndirectly, so every entry here is non-dispatchable. It is GET /v1/tool narrowed\nto one source,\ \ not a second store: a name a caller sees here is the same entry,\nwith the same activation state, that discovery\ \ reports." operationId: get_tool_skills parameters: - description: 'Activated keeps only the tools activated for the caller''s org and project, and only when it is exactly the string "true".' in: query name: activated required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tool.sourceToolList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the skills the caller's org can reach \u2014 the brand's embedded catalogue plus the org's own authored\ \ ones \u2014 with each one's activation flag." tags: - tool x-app: tool x-public: true x-tool: true post: description: "Adds or revises one of the caller org's own skills, and answers 201\nwith the stored record. The id is\ \ derived from the name, so writing the same\nname again REVISES that skill rather than accumulating near-duplicates\ \ that\nwould then collide in the registry. An org's skills are private to it by\nconstruction \u2014 they live in\ \ a different store from the brand's embedded\ncatalogue and have no path into the public gallery \u2014 and a brand\ \ skill always\nwins a name collision against an org's.\n\nAn activated skill is carried into every agent run in the\ \ org, so writing one\ntakes an admin of the org or a SuperAdmin; a member is refused 403. The write is\non the org's\ \ audit trail before it is made, and one the trail cannot record is\nrefused 503." operationId: post_tool_skills requestBody: content: application/json: example: content: "# Triage\n\u2026" description: how we triage name: triage schema: $ref: '#/components/schemas/tool.skillIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/tool.skillWritten' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Adds or revises one of the caller org's own skills, and answers 201 with the stored record. tags: - tool x-app: tool x-public: true x-tool: true /v1/tool/skills/authored: get: description: "Lists the caller org's OWN skills with their SKILL.md\nbodies. GET /v1/tool/skills is the registry view\ \ \u2014 the brand's catalogue plus this\norg's, with activation flags and no bodies; this is the EDITABLE set, so\ \ it\ncarries the content that view omits and nothing the org did not write." operationId: get_tool_skills_authored responses: '200': content: application/json: schema: $ref: '#/components/schemas/tool.authoredSkillList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the caller org's OWN skills with their SKILL.md bodies. tags: - tool x-app: tool x-public: true x-tool: true /v1/tool/skills/{id}: delete: description: "Removes one of the caller org's authored skills. Scoped to the\ncaller's org, so an id belonging to another\ \ tenant is never reached. Removing\nwhat is not there is not an error \u2014 the caller's intent is \"gone\", and\ \ it is.\nLike writing one it takes an admin of the org or a SuperAdmin, and is on the\norg's audit trail before it\ \ is made." operationId: delete_tool_skills_by_id parameters: - description: ID is the skill to remove, from the path. It is the skill's name. in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/tool.skillDeleted' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one of the caller org's authored skills. tags: - tool x-app: tool x-public: true x-tool: true /v1/translate: post: description: "Returns one translation per input string, in input order, each carrying where it sits on the review ladder\ \ and whether it came from your memory rather than an engine \u2014 plus a usage block of REAL counts (strings, cached,\ \ translated, and the source characters that actually reached an engine). Send `text` for one string or `batch` for\ \ many, never both. When you name no `source`, the detected one is reported back.\n\nTHE TRANSLATION MEMORY IS CONSULTED\ \ FIRST AND IT IS NORMATIVE, NOT A CACHE. Every string keys on (source text, target, glossary version, tier); a hit\ \ is returned VERBATIM and never re-translated, which is what makes a locale rebuild idempotent under a non-deterministic\ \ model and the bill proportional to what actually changed. Misses go to the engine and are written back at state\ \ `machine`. Editing a glossary term changes the key, so a stale rendering can never be served.\n\nIT CANNOT TRAMPLE\ \ REVIEWED WORK. A write from this route may create an entry or refresh one still at `machine`, and nothing else \u2014\ \ a string a human moved to approved or published through the memory review lane survives every rebuild, and comes\ \ back here unchanged. The memory is the caller's OWN org's, a separate store per org: the source text you send is\ \ customer content and lands nowhere else. Read it back or review it at /v1/translate/memory.\n\n`tier` picks the\ \ engine and defaults to quality \u2014 the model plane, which carries context, terminology and tone, and which bills\ \ its own tokens, so nothing is charged twice here. `bulk` is the high-volume engine and is metered HERE, on the source\ \ characters that reached it: a fully-cached rebuild reports zero characters and costs zero. BULK NEVER FALLS BACK\ \ TO QUALITY \u2014 on a deployment that does not serve it the answer is 503 for that tier, so a caller is never quietly\ \ served, or charged, at a tier it did not ask for. A bulk request beyond its balance is refused with the nested {\"\ error\":{\"code\",\"message\"}} body at 402/503.\n\n`target` IS CHECKED FOR SHAPE, NOT FOR SUPPORT: anything BCP-47-shaped\ \ is accepted (`es`, `pt-BR`), anything else is 400. There is no unsupported-language error \u2014 a well-formed tag\ \ no engine can actually render is passed straight through, and whatever comes back is what gets stored and returned.\ \ `format` (text, html, markdown) tells the engine what markup to preserve; `glossary` fixes terms verbatim.\n\nRequires\ \ a validated principal \u2014 401 without one, and the org is always that principal's. At most 512 strings per call\ \ and 32768 characters per string; an engine that fails or answers a reply that does not cover every input is 502,\ \ and nothing is stored." operationId: post_translate summary: Translate a string or a batch into one target language tags: - translate x-app: translate x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/translate/memory: get: description: "Returns the org's own translation-memory entries, newest first, optionally\nnarrowed to one target language\ \ and/or one position on the review ladder. It is\nthe review lane's read: what a human reviewer works through.\n\n\ The org is ALWAYS the validated principal's org, never a request field, so one\ntenant can never read another's memory\ \ \u2014 the entries hold customer source text." operationId: get_translate_memory parameters: - description: Target narrows to one target language tag (BCP-47, e.g. "es" or "pt-BR"). in: query name: target required: false schema: type: string - description: 'State narrows to one position on the review ladder: machine, suggested, approved or published.' in: query name: state required: false schema: type: string - description: 'Limit caps the rows returned. Non-positive or unparseable means the server default (200); the ceiling is 1000.' in: query name: limit required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/translate.MemoryPage' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the org's own translation-memory entries, newest first, optionally narrowed to one target language and/or one position on the review ladder. tags: - translate x-app: translate x-public: true x-tool: true put: description: "Records a human decision on one translation-memory entry, and returns the\nentry as stored. A human write\ \ always wins over the stored value, and once it lands\nat approved or published no machine write can move it again\ \ \u2014 which is what makes a\nlocale rebuild safe to run against reviewed work.\n\nThe org is ALWAYS the validated\ \ principal's org, never a request field, so a review\ncan only ever land in the caller's own memory." operationId: put_translate_memory requestBody: content: application/json: example: source: Hello state: approved target: es text: Hola schema: $ref: '#/components/schemas/translate.ReviewRequest' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/translate.MemoryEntry' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Records a human decision on one translation-memory entry, and returns the entry as stored. tags: - translate x-app: translate x-public: true x-tool: true /v1/treasury: get: description: "Returns the reserve fund's health and the current revenue-share\npolicy for any validated caller. It is\ \ a TRANSPARENCY view \u2014 a partner or\nauthor can see that the pool backing their payouts is solvent \u2014 and\ \ NOT per-org\nmoney, which is the customer's own commerce balance at /v1/billing/balance. The\npolicy is read-only\ \ here; only a SuperAdmin sets it." operationId: get_treasury responses: '200': content: application/json: schema: $ref: '#/components/schemas/treasury.TreasuryReport' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the reserve fund's health and the current revenue-share policy for any validated caller. tags: - treasury x-app: treasury x-public: true x-tool: true /v1/treasury/accounts: get: description: "Returns the ledger accounts the caller may see, with their\nbalances. It is tenant-isolated SERVER-SIDE:\ \ an ordinary caller sees ONLY\naccounts under its own \"org::\" prefix, never house accounts and never\n\ another tenant's. A SuperAdmin may widen with ?scope=house (the reserve,\nrevenue and payout house accounts) or ?org=\ \ \u2014 the only way to cross the\ntenant boundary, and only for platform sudo. The answer is honestly empty until\n\ a tenant has ledger postings." operationId: get_treasury_accounts parameters: - description: Scope is "house" to read the reserve/revenue/payout house accounts. SuperAdmin only. in: query name: scope required: false schema: type: string - description: Org names another tenant to read. SuperAdmin only; ignored when scope=house. in: query name: org required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/treasury.accountsOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the ledger accounts the caller may see, with their balances. tags: - treasury x-app: treasury x-public: true x-tool: true /v1/trust: get: description: 'Reads YOUR organization''s whole trust centre, including the addresses of your own gated documents. Same shape as the published endpoint; the difference is that this one is resolved from your validated bearer and shows you your own artifacts.' operationId: get_trust responses: '200': content: application/json: schema: $ref: '#/components/schemas/trust.centre' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads YOUR organization's whole trust centre, including the addresses of your own gated documents. tags: - trust x-app: trust x-public: true x-tool: true /v1/trust/controls: get: description: "Lists every control your organization publishes, with the counts.\n\nA control names what it asserts,\ \ the mechanism behind it, the repository and\nfile where that mechanism is enforced, how it is verified, and the\ \ framework\nclauses it maps to. Status is automated, partial or absent \u2014 and an absent one\nstill names the\ \ clause it would satisfy, which is a roadmap, while never\nmoving a coverage number." operationId: get_trust_controls responses: '200': content: application/json: schema: $ref: '#/components/schemas/trust.controlList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists every control your organization publishes, with the counts. tags: - trust x-app: trust x-public: true x-tool: true /v1/trust/controls/{id}: get: description: Reads one control by id. operationId: get_trust_controls_by_id parameters: - description: "ID is the control's id, dotted lowercase \u2014 \"iam.pkce.s256\"." example: iam.pkce.s256 in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads one control by id. tags: - trust x-app: trust x-public: true x-tool: true /v1/trust/coverage: get: description: "Reads coverage: per framework, how many clauses have an automated control\nbehind them, how many are partial,\ \ and how many have none \u2014 each carrying the\nunit it is counted in, because \"12 of 20\" is not a fact until\ \ you know what\nthe 20 are.\n\nNothing here is a verdict. There is no boolean, and a control that only a\nperson\ \ has read counts one rung weaker than it claims to be, because only a\ncheck that can FAIL is evidence." operationId: get_trust_coverage responses: '200': content: application/json: schema: $ref: '#/components/schemas/trust.trustCoverage' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Reads coverage: per framework, how many clauses have an automated control behind them, how many are partial,\ \ and how many have none \u2014 each carrying the unit it is counted in, because \"12 of 20\" is not a fact until\ \ you know what the 20 are." tags: - trust x-app: trust x-public: true x-tool: true /v1/trust/coverage/{framework}: get: description: "Reads one framework clause by clause: every clause the standard publishes,\nwhat covers it, and which\ \ controls stand behind it \u2014 so a coverage number can\nbe checked line by line rather than taken on trust." operationId: get_trust_coverage_by_framework parameters: - description: "Framework is the framework id \u2014 \"soc2\", \"iso27001\", \"nist80053\"." example: soc2 in: path name: framework required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/trust.clauseCoverage' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Reads one framework clause by clause: every clause the standard publishes, what covers it, and which controls\ \ stand behind it \u2014 so a coverage number can be checked line by line rather than taken on trust." tags: - trust x-app: trust x-public: true x-tool: true /v1/trust/documents: get: description: 'Lists your organization''s documents. Because this is your own centre, a gated artifact carries its address here; through the published endpoint it does not.' operationId: get_trust_documents responses: '200': content: application/json: schema: $ref: '#/components/schemas/trust.trustDocuments' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists your organization's documents. tags: - trust x-app: trust x-public: true x-tool: true /v1/trust/evidence: get: description: "Reads the audit rows that stand behind one control, over a window.\n\nThe inventory decides what evidences\ \ what: a control names the audit actions\nthat are its trail, and this resolves the control id to those actions and\n\ reads them. So evidence cannot drift from the inventory, and it is scoped to\nyour own organization \u2014 the query\ \ carries no organization field for a caller\nto fill in.\n\nA control that nothing in the trail evidences says so\ \ plainly rather than\nanswering an empty page, because an empty page reads like a clean quarter. A\ndeployment with\ \ no audit store answers 501 and says the trail was not read,\nfor the same reason." operationId: get_trust_evidence parameters: - description: Control is the control id whose trail to read. Required. example: iam.refresh.rotation in: query name: control required: false schema: type: string - description: 'From is the inclusive lower bound, an RFC 3339 date or instant ("2026-01-01" or "2026-01-01T00:00:00Z"). Empty leaves it unbounded. A malformed bound is refused rather than silently widening the window.' example: '2026-01-01' in: query name: from required: false schema: type: string - description: To is the upper bound, same form and same tolerance. in: query name: to required: false schema: type: string - description: 'Limit caps the rows returned, 1..1000, default 100. It is a string because an unparseable value is refused rather than read as zero.' example: '50' in: query name: limit required: false schema: type: string responses: '200': content: application/json: schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads the audit rows that stand behind one control, over a window. tags: - trust x-app: trust x-public: true x-tool: true /v1/trust/faq: get: description: "Lists your knowledge base \u2014 the questions a reviewer asks, answered once." operationId: get_trust_faq responses: '200': content: application/json: schema: $ref: '#/components/schemas/trust.faqList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists your knowledge base \u2014 the questions a reviewer asks, answered once." tags: - trust x-app: trust x-public: true x-tool: true /v1/trust/frameworks: get: description: 'Lists the frameworks coverage is computed against, and how many clauses each publishes. That count is the denominator of every coverage number, which is what keeps an uncovered clause visible instead of dropping out of the fraction.' operationId: get_trust_frameworks responses: '200': content: application/json: schema: $ref: '#/components/schemas/trust.frameworkList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the frameworks coverage is computed against, and how many clauses each publishes. tags: - trust x-app: trust x-public: true x-tool: true /v1/trust/policies: get: description: Lists your organization's published policies. operationId: get_trust_policies responses: '200': content: application/json: schema: $ref: '#/components/schemas/trust.policyList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists your organization's published policies. tags: - trust x-app: trust x-public: true x-tool: true /v1/trust/profile: get: description: "Reads your organization's trust-centre profile \u2014 the name, tagline and\nsummary a visitor sees, whether\ \ the centre is published, and where to send\nsomebody who wants a gated document." operationId: get_trust_profile responses: '200': content: application/json: schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Reads your organization's trust-centre profile \u2014 the name, tagline and summary a visitor sees, whether\ \ the centre is published, and where to send somebody who wants a gated document." tags: - trust x-app: trust x-public: true x-tool: true /v1/trust/published/{org}: get: description: "Reads a published trust centre \u2014 the whole thing in one answer: the\norganization's profile, its\ \ control inventory, coverage computed against each\nframework's whole published clause list, its documents, subprocessors,\n\ policies, knowledge base, updates and risk profile.\n\nThis is the PUBLIC endpoint and needs no credential, because\ \ a published trust\ncentre is a public document. It answers only for an organization that has\npublished one \u2014\ \ an organization that has not is not found rather than empty,\nsince an empty centre and a centre nobody meant to\ \ show read the same and are\nnot the same thing.\n\nA gated document appears here with its title, its type and its\ \ date and NO\naddress: the listing says the artifact exists and that reading it takes a\ngrant. Nothing an independent\ \ auditor signed is ever released through this\nendpoint." operationId: get_trust_published_by_org parameters: - description: "Org is the organization's slug \u2014 the name in its address." example: hanzo in: path name: org required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/trust.centre' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused security: [] summary: "Reads a published trust centre \u2014 the whole thing in one answer: the organization's profile, its control\ \ inventory, coverage computed against each framework's whole published clause list, its documents, subprocessors,\ \ policies, knowledge base, updates and risk profile." tags: - trust x-app: trust x-public: true x-tool: true /v1/trust/risk: get: description: "Reads your risk profile \u2014 the label and value pairs describing what your\norganization handles and\ \ how." operationId: get_trust_risk responses: '200': content: application/json: schema: {} description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Reads your risk profile \u2014 the label and value pairs describing what your organization handles and how." tags: - trust x-app: trust x-public: true x-tool: true /v1/trust/subprocessors: get: description: 'Lists the third parties your organization sends data to, each naming what it is for.' operationId: get_trust_subprocessors responses: '200': content: application/json: schema: $ref: '#/components/schemas/trust.subprocessorList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists the third parties your organization sends data to, each naming what it is for. tags: - trust x-app: trust x-public: true x-tool: true /v1/trust/updates: get: description: Lists your trust-centre updates, newest as you ordered them. operationId: get_trust_updates responses: '200': content: application/json: schema: $ref: '#/components/schemas/trust.updateList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Lists your trust-centre updates, newest as you ordered them. tags: - trust x-app: trust x-public: true x-tool: true /v1/trust/{kind}/{id}: delete: description: 'Removes one record from a section of your organization''s trust centre. A record that is not there is a 404, never a silent success. A control that belongs to the deployment''s own inventory is removed by a commit, not by a request.' operationId: delete_trust_by_kind_by_id parameters: - description: "Kind is the section \u2014 profile, control, document, subprocessor, policy,\nfaq, update or risk. Anything\ \ else is not found. The URL is the authority:\na value here is bound from the path, which zip binds last." example: faq in: path name: kind required: true schema: type: string - description: 'ID is the record''s id within that section. The single-valued sections (profile, risk) hold one record whatever id is named.' example: where-is-data-held in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/trust.dropped' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one record from a section of your organization's trust centre. tags: - trust x-app: trust x-public: true x-tool: true put: description: "Writes one record into a section of YOUR organization's trust centre \u2014\nprofile, control, document,\ \ subprocessor, policy, faq, update or risk.\n\nA control written here is held to exactly the rule a control committed\ \ to the\ndeployment's own inventory is held to, by the same validator: its prose may\nnot claim a certificate and\ \ may not name a framework (a framework belongs in\nthe mappings, where it arrives attached to a number), anything\ \ short of\nautomated must say what is missing, and a mapping to a clause no framework\ndeclares is refused rather\ \ than scored as nothing.\n\nA document defaults to GATED. An artifact an independent auditor signed \u2014 a\nSOC\ \ 2 report, an ISO certificate, a penetration test, an auditor letter \u2014\ncannot be made public at all; it is\ \ released through a grant. A\nself-assessment can, because the organization is the one attesting it.\n\nThe deployment's\ \ OWN control inventory is governed in git and is not writable\nhere: naming one of its ids is a conflict, not an\ \ overwrite." operationId: put_trust_by_kind_by_id parameters: - description: Kind is the section being written. The URL is the authority. example: subprocessor in: path name: kind required: true schema: type: string - description: 'ID is the record''s id. Omit it on a create and one is minted; the single-valued sections (profile, risk) hold one record whatever is named.' example: acme-cloud in: path name: id required: true schema: type: string requestBody: content: application/json: example: data: name: A cloud purpose: Compute id: acme-cloud kind: subprocessor schema: $ref: '#/components/schemas/trust.sectionWrite' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/trust.written' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Writes one record into a section of YOUR organization's trust centre \u2014 profile, control, document, subprocessor,\ \ policy, faq, update or risk." tags: - trust x-app: trust x-public: true x-tool: true /v1/usage: post: description: "Ingests a batch of account-usage samples \u2014 what a developer's OWN AI\naccounts have consumed of their\ \ OWN plans, metered from each provider's own\nlogin \u2014 and appends them to the warehouse series. Answers 202.\n\ \nSend either a `samples` array or one sample's fields at the top level. Every\nsample needs a provider, a machine\ \ and a known window class; an unknown window or\nkind is refused rather than silently rewritten, because a dash filled\ \ with a class\nnobody reported is worse than an error. There is no timestamp field: the server\nowns the observation\ \ clock, and a sample says which window it measured with\nwindowStart or resetsAt.\n\nIt is FAIL-SOFT on storage:\ \ a warehouse outage costs a poll of history\n(stored:false), never a failed request. It records usage ONLY \u2014\ \ the link registry\nis refreshed separately via POST /v1/link, so there is one and only one way to\nupdate an account\ \ row." operationId: post_usage requestBody: content: application/json: schema: $ref: '#/components/schemas/usage.reportReq' required: true responses: '202': content: application/json: schema: $ref: '#/components/schemas/usage.reportResp' description: accepted default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Ingests a batch of account-usage samples \u2014 what a developer's OWN AI accounts have consumed of their\ \ OWN plans, metered from each provider's own login \u2014 and appends them to the warehouse series." tags: - usage x-app: usage x-public: true x-tool: true /v1/usage/analytics: get: description: "Is the entitlement-GATED per-provider breakdown of the caller org's LLM\nusage \u2014 the paid lens over\ \ the same warehouse ledger GET /v1/usage/summary reads\nits totals from. Basic own-org usage stays ungated at /v1/usage/summary.\n\ \nA plan that does not grant the analytics datastore is refused with 402, and an\nunresolvable plan fails closed to\ \ the free floor, which does not grant it. The\nwindow is clamped forward to the plan's retention entitlement, so\ \ a tenant can\nnever read older than its plan allows even with a custom start. The response is\nmarked no-store.\n\ \nINTERIM (mirrors apps/world's limits echo): no org\u2192plan resolver exists in cloud\nyet \u2014 the subscription\ \ lookup is owned by the billing plane and the gateway\nprincipal carries no plan claim \u2014 so the caller passes\ \ the plan and the gate\nresolves THAT plan's access." operationId: get_usage_analytics parameters: - description: End is the exclusive window end, RFC3339. Read only when Range is custom. in: query name: end required: false schema: type: string - description: 'Plan is the plan id whose entitlement decides access and retention. INTERIM: cloud has no org-to-plan resolver yet, so the caller names the plan; when that resolver lands this becomes the caller org''s own plan.' in: query name: plan required: false schema: type: string - description: "Range is the window: a count and a unit \u2014 24h, 7d, 90d, any h or d \u2014\nor day, week,\ \ month, all, custom. Empty means 24h. The window is then\nclamped forward to the plan's retention entitlement." in: query name: range required: false schema: type: string - description: 'Start is the inclusive window start, RFC3339. Read only when Range is custom, and clamped forward to the plan''s retention floor.' in: query name: start required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/usage.usageAnalyticsView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Is the entitlement-GATED per-provider breakdown of the caller org's LLM usage \u2014 the paid lens over the\ \ same warehouse ledger GET /v1/usage/summary reads its totals from." tags: - usage x-app: usage x-public: true x-tool: true /v1/usage/analytics/access: get: description: "Echoes a plan's resolved analytics entitlement so a dashboard can\nconfigure itself against the LIVE catalog\ \ instead of hardcoding tier numbers. An\nempty plan resolves the free floor, and a catalog resolution failure serves\ \ that\nsame floor rather than erroring \u2014 so this always answers 200. It is a read-only\ncontract echo and carries\ \ no tenant data." operationId: get_usage_analytics_access parameters: - description: "Plan is a plan id from the live @hanzo/plans catalog. Empty resolves the\nfree floor, and so does an\ \ id the catalog does not know \u2014 this never fails\non an unknown plan." in: query name: plan required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/usage.usageAnalyticsAccess' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Echoes a plan's resolved analytics entitlement so a dashboard can configure itself against the LIVE catalog instead of hardcoding tier numbers. tags: - usage x-app: usage x-public: true x-tool: true /v1/usage/samples: get: description: "Is the PER-PROVIDER view: one connected account's own consumption of its\nown plan \u2014 \"my plan is\ \ 47% through its 6h window, resets at 14:20\".\n\n`current` is the newest instance of each lane (the headline); `windows`\ \ is the\nhistory behind it. Both come from ONE deduped read, so they can never disagree.\nThe rows are the caller's\ \ OWN linked accounts, scoped to the validated principal\nand its subject \u2014 never another user's, and never another\ \ org's." operationId: get_usage_samples parameters: - description: 'Account narrows to ONE linked account of that provider. Empty covers every account the caller has linked there.' in: query name: account required: false schema: type: string - description: Provider is the upstream to read, e.g. anthropic. Required. in: query name: provider required: false schema: type: string - description: "Range is the window to read: a count and a unit \u2014 1h, 24h, 90d, any h or\nd \u2014 or day,\ \ week, month, all. Empty means 24h. A label that is not a\ncount, or one reaching past the 730-day horizon, is\ \ refused rather than\nsilently replaced." in: query name: range required: false schema: type: string - description: 'Window narrows to ONE window class: 6h, day, week or month. Empty covers every class.' in: query name: window required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/usage.dashResp' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Is the PER-PROVIDER view: one connected account's own consumption of its own plan \u2014 \"my plan is 47%\ \ through its 6h window, resets at 14:20\"." tags: - usage x-app: usage x-public: true x-tool: true /v1/usage/summary: get: description: "Answers GET /v1/usage/summary: the caller's own usage footprint over one\nwindow \u2014 the categorized\ \ spend roll-up from the commerce ledger, the org's LLM\nusage totals from the warehouse, and the caller's OWN linked\ \ provider accounts\nbeside the org's Hanzo-routed usage.\n\nEvery source degrades INDEPENDENTLY to honest zeros and\ \ says so in `sources` and\nin its own `available` flag, so a partial deploy reports \"no data\" rather than\nfabricating\ \ spend. The account rows and the Hanzo rows are concatenated and never\nsummed: a plan's percent is not money.\n\n\ The response is org-scoped from the validated principal and marked no-store \u2014 a\nsigned-out caller is refused." operationId: get_usage_summary parameters: - description: "Range is the window: a count and a unit \u2014 24h, 7d, 90d, any h or d \u2014\nor day, week,\ \ month, all, custom. Empty means 24h. A label this surface\ndoes not know, or one reaching past the 730-day horizon,\ \ is refused rather\nthan silently replaced." in: query name: range required: false schema: type: string - description: 'Start is the inclusive window start, RFC3339. Read only when Range is custom.' in: query name: start required: false schema: type: string - description: End is the exclusive window end, RFC3339. Read only when Range is custom. in: query name: end required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/usage.usageSummary' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Answers GET /v1/usage/summary: the caller's own usage footprint over one window \u2014 the categorized spend\ \ roll-up from the commerce ledger, the org's LLM usage totals from the warehouse, and the caller's OWN linked provider\ \ accounts beside the org's Hanzo-routed usage." tags: - usage x-app: usage x-public: true x-tool: true /v1/validator: get: description: "Returns the validator slots the caller's org has claimed.\n\nOne entry per claimed slot with its node\ \ identity, its live-ish node status and\nthe owner-gated registration queued for it, if any. Slots are org-scoped\ \ by the\nvalidated identity, so a caller can only ever see their own \u2014 a slot claimed by\nanother org is not\ \ merely hidden from this list, it is unreachable through the\nwhole surface." operationId: get_validator parameters: - description: 'Limit is how many slots to return, as a decimal string in the `?limit=` query. Absent, unparseable or non-positive means 200; over 1000 is clamped to 1000. It is a string rather than a number because the parse that has always served this route trims surrounding whitespace, and one parse rule is better than two.' in: query name: limit required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/validator.validatorList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the validator slots the caller's org has claimed. tags: - validator x-app: validator x-public: true x-tool: true post: description: "Claims a validator slot and provisions its node, after proving the\ncaller's wallet owns the slot's NFT.\n\ \nThe pipeline, all server-enforced: burn the single-use challenge (so a replayed\nor forged nonce dies before any\ \ chain read), recover the signer from the message\nthis server rebuilds, require that wallet to hold Validator-tier\ \ GenesisNFT\n#tokenId on Ethereum mainnet, generate a fresh luxd staking identity and seal it\ninto KMS, write a\ \ LuxNetwork CR for a NEW node, and ENQUEUE an owner-gated\nregistration. The registration is never auto-submitted\ \ to any P-Chain \u2014 the\nowner co-signs it out of band \u2014 and the stake weight is set at co-sign time,\nnever\ \ derived from the NFT.\n\nIt fails CLOSED at every gate: a bad signature, a non-owner, a non-tier slot or\nan unavailable\ \ KMS all leave no claim persisted and no key material exposed.\nRe-claiming a slot this org already holds re-applies\ \ the node CR and returns 200\nwith the existing identity (keys and NodeID are stable); a slot held by another\norg\ \ is 409. A cluster-less deployment still claims the slot, seals the keys and\nqueues the registration, reporting\ \ the node as \"node_pending\"." operationId: post_validator requestBody: content: application/json: example: nonce: "5f3a\u2026" signature: "0x\u2026" tokenId: 7 schema: $ref: '#/components/schemas/validator.validatorClaim' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/validator.slotView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Claims a validator slot and provisions its node, after proving the caller's wallet owns the slot's NFT. tags: - validator x-app: validator x-public: true x-tool: true /v1/validator/challenge: get: description: 'Issues the single-use nonce and the exact message a wallet must sign to claim a validator slot. The nonce is bound to (validated org, slot) and stored server-side, so a signature obtained for one org or one slot can never be replayed for another, and the message POST /v1/validator verifies is rebuilt from those same server facts rather than trusted from the caller. Redeem it with POST /v1/validator before it expires; it can be redeemed once. A tokenId outside the Validator tier is refused here rather than after signing.' operationId: get_validator_challenge parameters: - description: 'TokenID is the Validator-tier GenesisNFT token id, as a decimal string in the `?tokenId=` query. A value that is not a positive integer is 400. It is a string rather than a number because the parse that has always served this route trims surrounding whitespace, and one parse rule is better than two.' in: query name: tokenId required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/validator.challengeView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Issues the single-use nonce and the exact message a wallet must sign to claim a validator slot. tags: - validator x-app: validator x-public: true x-tool: true /v1/validator/{tokenId}: get: description: "Returns one claimed validator slot, scoped to the caller's org.\n\nA slot another org holds, and a slot\ \ nobody holds, are both 404 \u2014 never a\ndifferent status, so this route cannot be used to probe which slots are\ \ taken." operationId: get_validator_by_tokenid parameters: - description: 'TokenID is the slot''s GenesisNFT token id, from the path, as a decimal string. A value that is not a positive integer is 400. It is a string rather than a number because the parse that has always served this route trims surrounding whitespace, and one parse rule is better than two.' in: path name: tokenId required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/validator.slotView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one claimed validator slot, scoped to the caller's org. tags: - validator x-app: validator x-public: true x-tool: true /v1/videos/generations: post: description: "Implements POST /v1/videos/generations \u2014 the ASYNC create.\n\nBody: {\"model\": \"...\", \"prompt\"\ : \"...\", \"size\"?: \"1280x720\", \"seconds\"?: int}\n\nIt authenticates the caller, resolves the model to its upstream\ \ provider via\nthe shared routing table (zen3-video* / wan2-2-t2v-a14b \u2192 the spark-video\nbackend), reserves\ \ the per-video budget (the balance gate), creates ONE\nupstream job, registers it in the in-pod store, and returns\ \ the OpenAI-shaped\nvideo object with status \"queued\" IMMEDIATELY. The client then polls\nGET /v1/videos/{id} and\ \ downloads GET /v1/videos/{id}/content. Nothing is\nbilled here \u2014 the debit lands on completion." operationId: post_videos_generations responses: '200': content: application/json: schema: $ref: '#/components/schemas/ai.VideoStatus' description: Success. '401': description: No credential, or one this service does not accept. '403': description: A valid credential that may not do this. summary: "Implements POST /v1/videos/generations \u2014 the ASYNC create." tags: - ai x-app: ai x-public: true /v1/videos/{id}: get: description: "Implements GET /v1/videos/{id} \u2014 poll a job's status.\n\nIt authenticates the caller, verifies they\ \ OWN the job (the caller's billing\nsubject must equal the job's), performs ONE upstream status poll, and \u2014\ \ the\nfirst time the job is observed completed \u2014 settles the reservation with the\nactual cost and records the\ \ billable usage event (exactly once). Returns the\nOpenAI-shaped video object." operationId: get_videos_by_id parameters: - in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/ai.VideoStatus' description: Success. '401': description: No credential, or one this service does not accept. '403': description: A valid credential that may not do this. summary: "Implements GET /v1/videos/{id} \u2014 poll a job's status." tags: - ai x-app: ai x-public: true /v1/videos/{id}/content: get: description: "Implements GET /v1/videos/{id}/content \u2014 download the finished MP4.\n\nIt authenticates + ownership-checks\ \ the caller, then proxies the upstream\n/content endpoint (bounded by the download concurrency ceiling) and streams\ \ the\nraw video bytes back inline. A successful download also bills the job once (for\nthe client that downloads\ \ without first polling to completion) \u2014 idempotent\nwith the poll path via job.markCompleted." operationId: get_videos_by_id_content parameters: - in: path name: id required: true schema: type: string summary: "Implements GET /v1/videos/{id}/content \u2014 download the finished MP4." tags: - ai x-app: ai x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/wallet: get: description: "Returns the caller org's wallets, newest first, optionally NARROWED\nwithin the org by project, agent\ \ or account. The org is always the bound\nisolation boundary \u2014 the filters only ever narrow inside it, so a\ \ caller can\nnever widen past its own org." operationId: get_wallet parameters: - description: Project narrows to wallets scoped to one project. Must be a url-safe segment. in: query name: project required: false schema: type: string - description: Agent narrows to wallets scoped to one agent. Must be a url-safe segment. in: query name: agent required: false schema: type: string - description: Account narrows to wallets under one account id. Must be a url-safe segment. example: acct_9f8c1d in: query name: account required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/wallet.walletList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the caller org's wallets, newest first, optionally NARROWED within the org by project, agent or account. tags: - wallet x-app: wallet x-public: true x-tool: true post: description: "Provisions a new signing identity under one of the caller org's\naccounts and answers the stored wallet\ \ including its on-chain address. The\ncustody backend generates the key material \u2014 a KMS-sealed secp256k1 key,\ \ an\nMPC threshold key on the ring, or a Safe smart wallet owned by one \u2014 and the\nHANDLE to it is kept server-side\ \ and never returned. A custody kind the\ndeployment has not wired fails CLOSED with 503: a signature is never\nfabricated.\ \ The wallet is scoped to the org, the caller's ambient project, and\noptionally an agent and the named account; those\ \ narrowings are what its key\nref is derived from, so each must be a url-safe segment." operationId: post_wallet requestBody: content: application/json: example: accountId: acct_9f8c1d chain: eip155:36963 custody: kms name: ops hot wallet tier: hot schema: $ref: '#/components/schemas/wallet.createWalletIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/wallet.Wallet' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Provisions a new signing identity under one of the caller org's accounts and answers the stored wallet including its on-chain address. tags: - wallet x-app: wallet x-public: true x-tool: true /v1/wallet/accounts: get: description: 'Returns the caller org''s wallet accounts, newest first. Accounts are physically org-scoped, so another tenant''s are not reachable from here.' operationId: get_wallet_accounts responses: '200': content: application/json: schema: $ref: '#/components/schemas/wallet.accountList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns the caller org's wallet accounts, newest first. tags: - wallet x-app: wallet x-public: true x-tool: true post: description: 'Opens a named wallet account for the caller''s org. An account is a GROUPING of wallets, not a key or a balance: wallets are created under one and can be listed by it. The org is stamped by the server from the validated principal, so a request can never open an account in another tenant.' operationId: post_wallet_accounts requestBody: content: application/json: example: name: treasury schema: $ref: '#/components/schemas/wallet.createAccountIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/wallet.WalletAccount' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Opens a named wallet account for the caller's org. tags: - wallet x-app: wallet x-public: true x-tool: true /v1/wallet/{id}: get: description: 'Returns one of the caller org''s wallets: its scope, custody kind, tier, chain and on-chain address. The custody handle to the signing material is never part of the answer. A wallet id another org owns reads as not found, so the response cannot confirm that it exists.' operationId: get_wallet_by_id parameters: - example: wal_4b1e77 in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/wallet.Wallet' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns one of the caller org''s wallets: its scope, custody kind, tier, chain and on-chain address.' tags: - wallet x-app: wallet x-public: true x-tool: true /v1/wallet/{id}/keys: post: description: "Rolls one wallet's signing material through its own custody backend\nand answers the wallet with whatever\ \ address that produced. For KMS custody a\nfresh secp256k1 key is generated and sealed, which CHANGES the address\ \ \u2014 funds\nand approvals at the old address do not move. For a Safe the address is\ncounterfactual and the owner\ \ shares are ring-managed, so rotation is a no-op\nand the address is unchanged. A backend that is not configured\ \ fails closed\nwith 503 rather than leaving the wallet half-rotated." operationId: post_wallet_by_id_keys parameters: - example: wal_4b1e77 in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/wallet.Wallet' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Rolls one wallet's signing material through its own custody backend and answers the wallet with whatever address that produced. tags: - wallet x-app: wallet x-public: true x-tool: true /v1/wallet/{id}/sign: post: description: "Produces a secp256k1 signature from one of the caller org's wallets over\na 32-byte digest, through whichever\ \ custody backend that wallet uses. Give it\neither a `digest` (32 bytes as hex, signed verbatim) or a `message` (hashed\n\ with Keccak256 first) \u2014 exactly one is required. The private key never leaves\nits backend: KMS custody opens\ \ the sealed key in-process, MPC custody produces\na threshold signature on the ring. The answer carries the digest\ \ that was\nsigned alongside the signature, so a caller can verify what it got." operationId: post_wallet_by_id_sign parameters: - example: wal_4b1e77 in: path name: id required: true schema: type: string requestBody: content: application/json: example: id: wal_4b1e77 message: approve withdrawal 42 schema: $ref: '#/components/schemas/wallet.signIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/wallet.signature' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Produces a secp256k1 signature from one of the caller org's wallets over a 32-byte digest, through whichever custody backend that wallet uses. tags: - wallet x-app: wallet x-public: true x-tool: true /v1/wallet/{id}/transactions: post: description: "Composes a Safe transaction on the MPC ring and answers its\nEIP-712 hash together with the owner approval\ \ the ring's threshold signature\nproduced. Only a wallet whose custody is \"safe\" can do this \u2014 any other custody\n\ is a 400, because the backend itself is asked whether it can propose rather\nthan the kind being switched on. The\ \ ring computes the Safe-tx hash bound to\nthe Safe contract and the chain id, so the hash a caller gets back is the\ \ one\nthe Safe will verify. This PROPOSES: it does not execute the transaction." operationId: post_wallet_by_id_transactions parameters: - example: wal_4b1e77 in: path name: id required: true schema: type: string requestBody: content: application/json: example: chainId: 36963 data: 0x id: wal_4b1e77 nonce: 7 to: '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984' value: '0' schema: $ref: '#/components/schemas/wallet.safeTxIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/wallet.safeProposal' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Composes a Safe transaction on the MPC ring and answers its EIP-712 hash together with the owner approval the ring's threshold signature produced. tags: - wallet x-app: wallet x-public: true x-tool: true /v1/web3/chains: get: description: "Reports the chains this deployment can reach. The list is the\ndeclared registry, so it is exactly what\ \ /v1/web3/rpc will accept \u2014 a chain that\nappears here is one this deployment actually has an upstream for." operationId: get_web3_chains responses: '200': content: application/json: schema: $ref: '#/components/schemas/web3.chainList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports the chains this deployment can reach. tags: - web3 x-app: web3 x-public: true x-tool: true /v1/web3/chains/{chain}: get: description: "Reports one chain and whether its upstream is answering. An\nunreachable chain is still a 200 with live:false\ \ \u2014 the chain is configured,\nwhich is a different fact from the chain being up, and a 502 here would make\n\ a console page error rather than show the outage." operationId: get_web3_chains_by_chain parameters: - description: Chain is the registry id, as in /v1/web3/chains/lux. in: path name: chain required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/web3.chainStatus' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reports one chain and whether its upstream is answering. tags: - web3 x-app: web3 x-public: true x-tool: true /v1/web3/rpc/{chain}: post: description: "Forwards a JSON-RPC call to the named chain and returns its answer\nunchanged. Only declared chains are\ \ reachable, and only to a caller with a\nvalidated principal \u2014 this is the deployment's upstream, not an open\ \ relay." operationId: post_web3_rpc_by_chain parameters: - description: Chain is the registry id, from the URL. in: path name: chain required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/web3.rpcIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/web3.rpcOut' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Forwards a JSON-RPC call to the named chain and returns its answer unchanged. tags: - web3 x-app: web3 x-public: true x-tool: true /v1/web3/tokens/{chain}/{address}: get: description: "Reads an address's native balance on a chain.\n\nERC-20 positions are NOT enumerated here: eth_getBalance\ \ answers the native\none, but \"every token this address holds\" is an indexer question \u2014 there is no\nRPC call\ \ that answers it, and walking a token list would return a number that\nsilently omits whatever the list missed. explorer\ \ owns the indexer\nrelationship; this returns the balance the chain itself can prove." operationId: get_web3_tokens_by_chain_by_address parameters: - description: Chain is the registry id. in: path name: chain required: true schema: type: string - description: Address is the account, 0x-prefixed. in: path name: address required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/web3.balances' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads an address's native balance on a chain. tags: - web3 x-app: web3 x-public: true x-tool: true /v1/webhook: get: description: "Returns every webhook endpoint the caller's org has registered,\nnewest first, each with its 7-day delivery\ \ and failure counts. Signing secrets\nare redacted here \u2014 a secret leaves the server only on create and on rotate.\n\ The listing is physically org-scoped, so another tenant's endpoints are not\nreachable from this route at all." operationId: get_webhook responses: '200': content: application/json: schema: $ref: '#/components/schemas/webhook.endpointList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns every webhook endpoint the caller's org has registered, newest first, each with its 7-day delivery and failure counts. tags: - webhook x-app: webhook x-public: true x-tool: true post: description: "Registers a new webhook subscription for the caller's org and\nanswers 201 with the endpoint INCLUDING\ \ its freshly minted signing secret.\nThis is one of only two responses that ever carry that secret (the other is\n\ rotate) \u2014 store it now, because no later read returns it. The org is stamped by\nthe server from the validated\ \ principal, so a body can never register an\nendpoint in another tenant." operationId: post_webhook requestBody: content: application/json: example: description: order pipeline events: - commerce.order.> url: https://acme.example/hooks/hanzo schema: $ref: '#/components/schemas/webhook.createEndpointIn' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/webhook.Endpoint' description: created default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Registers a new webhook subscription for the caller's org and answers 201 with the endpoint INCLUDING its freshly minted signing secret. tags: - webhook x-app: webhook x-public: true x-tool: true /v1/webhook/{id}: delete: description: 'Removes one of the caller org''s webhook endpoints and answers 204 with no body. Delivery stops immediately and the endpoint''s signing secret is gone with it; its recorded delivery history goes too. An id another org owns reads as not found.' operationId: delete_webhook_by_id parameters: - example: wh_9f8c1d2e in: path name: id required: true schema: type: string responses: '204': description: no content default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Removes one of the caller org's webhook endpoints and answers 204 with no body. tags: - webhook x-app: webhook x-public: true x-tool: true get: description: 'Returns one of the caller org''s webhook endpoints with its 7-day delivery and failure counts, signing secret redacted. An id another org owns reads as not found, so the response cannot confirm that it exists.' operationId: get_webhook_by_id parameters: - example: wh_9f8c1d2e in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/webhook.Endpoint' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Returns one of the caller org's webhook endpoints with its 7-day delivery and failure counts, signing secret redacted. tags: - webhook x-app: webhook x-public: true x-tool: true put: description: "Replaces the editable fields of one of the caller org's\nendpoints \u2014 url, events, status and description\ \ \u2014 and answers the stored row\nwith its secret redacted. It is a full replace, not a patch: an omitted field\n\ is written as its empty value, and an omitted or empty events list resubscribes\nthe endpoint to EVERY event. The\ \ signing secret and the creation time are\nimmutable here; rotate the secret with POST /v1/webhook/{id}/secret." operationId: put_webhook_by_id parameters: - example: wh_9f8c1d2e in: path name: id required: true schema: type: string requestBody: content: application/json: example: events: - commerce.order.paid id: wh_9f8c1d2e status: disabled url: https://acme.example/hooks/v2 schema: $ref: '#/components/schemas/webhook.updateEndpointIn' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/webhook.Endpoint' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Replaces the editable fields of one of the caller org's endpoints \u2014 url, events, status and description\ \ \u2014 and answers the stored row with its secret redacted." tags: - webhook x-app: webhook x-public: true x-tool: true /v1/webhook/{id}/deliveries: get: description: "Returns one endpoint's per-attempt delivery log, newest first \u2014\nthe record of what was sent, what\ \ the subscriber answered, and how long it\ntook. One event that retried three times appears as three rows sharing\ \ a\ndelivery id. It is org-scoped exactly like every other route here: the endpoint\nlookup only ever finds THIS\ \ org's endpoint, so another org's id is a 404 and\nnever a window onto its logs." operationId: get_webhook_by_id_deliveries parameters: - example: wh_9f8c1d2e in: path name: id required: true schema: type: string - description: 'Limit caps how many attempts come back: default 50, maximum 200. A value that is not a positive integer reads as the default.' example: 100 in: query name: limit required: false schema: format: int64 type: integer - description: 'Status narrows the log to one outcome: "ok", "retrying" or "failed". Empty returns every attempt.' example: failed in: query name: status required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/webhook.deliveryList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns one endpoint's per-attempt delivery log, newest first \u2014 the record of what was sent, what the\ \ subscriber answered, and how long it took." tags: - webhook x-app: webhook x-public: true x-tool: true /v1/webhook/{id}/secret: post: description: "Mints a NEW HMAC signing secret for the endpoint and answers the\nendpoint WITH it \u2014 the only other\ \ response besides create that ever carries a\nsecret. The old secret stops working the instant this returns: every\ \ subsequent\ndelivery signs with the new one, with no overlap window. Call it when the\nsubscriber is ready to swap\ \ the value on its side, not before." operationId: post_webhook_by_id_secret parameters: - example: wh_9f8c1d2e in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/webhook.Endpoint' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Mints a NEW HMAC signing secret for the endpoint and answers the endpoint WITH it \u2014 the only other response\ \ besides create that ever carries a secret." tags: - webhook x-app: webhook x-public: true x-tool: true /v1/webhook/{id}/test: post: description: "Sends ONE signed test event to the endpoint right now and answers\nthe outcome inline, so the console\ \ can show whether the subscriber is reachable\nwithout waiting for real traffic. It takes the same attempt path the\ \ bus\ndispatcher takes \u2014 one attempt, 10s timeout, no retry ladder \u2014 and records the\nresult in the endpoint's\ \ delivery log. It works on a DISABLED endpoint too:\nvalidating one you have paused is the whole point." operationId: post_webhook_by_id_test parameters: - example: wh_9f8c1d2e in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/webhook.testResult' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Sends ONE signed test event to the endpoint right now and answers the outcome inline, so the console can show whether the subscriber is reachable without waiting for real traffic. tags: - webhook x-app: webhook x-public: true x-tool: true /v1/websearch: post: description: "Searches the live web and answers with ranked results.\n\nThis is the fleet's path to what is happening\ \ RIGHT NOW \u2014 today's weather, an\noutage, a release that postdates any model's training. `q` is the query and\n\ `language` narrows it to a locale. The answer is `{query, number_of_results,\nresults:[{url, title, content, engine}]}`,\ \ where `content` is the ENGINE's\nsnippet and not the page: read a page with POST /v1/crawl.\n\nIt is served in-process\ \ by a Go meta-search over keyless public engines \u2014 never\na third-party search API and never a search key. The\ \ enabled engines run\nconcurrently and their hits are merged, deduplicated by normalised URL (host\nand path, trailing\ \ slash and fragment dropped, query kept, so distinct queries\nstay distinct results) and capped at 30. Ranking is\ \ deterministic rather than\nscored: the first configured engine's hits lead.\n\nIt fails SOFT on the engines. One\ \ that errors, times out or is served a\nbot-challenge page contributes zero results and never fails the call, so\ \ an\nempty `results` is a real answer \u2014 nothing was found \u2014 and not an outage. The\narray is always present,\ \ never null.\n\nTwo refusals in the order they have to be asked, both in the PREAMBLE. A typed\nop is also an MCP\ \ tool, a call-plane operation, a graph field and a CLI\ncommand, and every one of those invokes it with no route\ \ and therefore no\nmiddleware \u2014 so what admits a caller here is asked where every caller reaches\nit rather\ \ than in a middleware only one of them passes through.\n\nA VALIDATED PRINCIPAL IS REQUIRED, and there is no tenant\ \ beyond that: the\nresults are public web pages, identical for every caller, so nothing here is\nscoped and nothing\ \ here can leak across orgs.\n\nTHEN THE ANTI-FORGERY TOKEN, immediately before the money, because that is\nwhat it\ \ is about. This search is the SAME bought meta-search the compat\nendpoint runs \u2014 the engines cost, and account.Shared/meter.go\ \ bills the\ncaller's ledger for the answer \u2014 so a page the caller never visited must not\nbe able to spend for\ \ them by sending their browser here with a cookie they\nalready hold. Nothing leaks; the answer is unreadable cross-origin.\ \ What\nmoves is money.\n\nIt is account's control, the one every operation in this estate asks, and it\nis a no-op\ \ the moment a caller PRESENTS a credential (Bearer, gateway, API\nkey) \u2014 which is every service and console\ \ caller here \u2014 so it costs a CLI, an\nagent and an API client nothing. Only the ambient-cookie path is asked\ \ for the\nechoed token. The raw /v1/websearch/search route asks the same control on its\ngroup (see Mount), so the\ \ two addresses of one search are admitted alike." operationId: search_web requestBody: content: application/json: schema: $ref: '#/components/schemas/websearch.webSearchQuery' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/websearch.webSearchResults' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Search the live web tags: - websearch x-app: websearch x-public: true x-tool: true /v1/websearch/scrape: post: description: "Takes {url} and answers {success, data:{markdown, metadata}} \u2014 the exact contract a firecrawl client\ \ decodes. The fetch, extraction and optional browser render run in-process; there is no crawler pod to be down.\n\ \nThe shared service key is required as an Authorization Bearer, compared in constant time: unset on the deployment\ \ is 503, missing or wrong is 401. Unlike search, a validated principal does NOT substitute for it \u2014 this is\ \ the service-to-service endpoint.\n\nA page is archived under the caller's own org and project, taken from the verified\ \ principal when there is one, so a scrape lands in the same corpus /v1/crawl fills and a URL already read under that\ \ scope is answered from the archive without touching the network. A service caller carrying no principal shares the\ \ unscoped prefix.\n\nThe URL is caller-supplied and fetched from INSIDE the cluster, which makes this a request-forgery\ \ primitive by construction: in-namespace service DNS and a cloud metadata endpoint that hands credentials to anyone\ \ who asks are both a resolution away. Only http and https are accepted, and every address actually dialled must be\ \ public unicast \u2014 loopback, link-local, private and multicast are refused. The check lives in the DIALER rather\ \ than on the hostname, because resolving a name to validate it and then letting the transport resolve it again is\ \ a gap DNS rebinding walks straight through; redirects re-enter the same dialer, so a public URL that bounces to\ \ the metadata address is refused at the hop that matters.\n\nThe one thing to get right: FAILURE IS 200. A missing\ \ or unparseable url, a body over the 1 MiB read cap, and a fetch that could not be completed all answer HTTP 200\ \ with success:false and a reason \u2014 a firecrawl client reads data.success, not the status line. Only the two\ \ auth refusals use a status code, so a caller that branches on HTTP status alone will read every failed scrape as\ \ a success." operationId: post_websearch_scrape summary: Fetch one page and get its extracted markdown, in the firecrawl envelope. tags: - websearch x-app: websearch x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/websearch/search: delete: description: "Answers {query, number_of_results, results:[{url, title, content, engine}]} \u2014 the exact /search?format=json\ \ contract a SearXNG client decodes, so an agent tool configured against SearXNG reaches this with no change. `q`\ \ is the query and `language` narrows it; both are read from the QUERY STRING.\n\nServed in-process by a Go meta-search\ \ over keyless public engines, never a third-party search API and never a search key. The enabled engines run concurrently\ \ and their hits are merged, deduplicated by normalised URL (host and path, trailing slash and fragment dropped, query\ \ kept \u2014 distinct queries are distinct results) and capped at 30. Ranking is deterministic rather than scored:\ \ the first configured engine's hits lead.\n\nTWO WAYS IN, one-way equivalent, and no third: a validated principal\ \ \u2014 the same gate the whole data plane uses \u2014 passes straight through, and a caller without one must present\ \ the shared service key as X-API-Key, compared in constant time. A deployment with no key configured answers 503\ \ rather than opening the surface to everyone, and a missing or wrong key is 401. It is never an open proxy. There\ \ is no tenant scoping beyond that gate, and there is nothing to scope: the results are public web pages, identical\ \ for every caller.\n\nIt fails SOFT on the engines and closed only on the gate. An engine that errors or is served\ \ a bot-challenge page contributes zero results and never fails the request, so an empty `results` is a real answer\ \ \u2014 nothing was found \u2014 and not an outage. The array is always present, never null.\n\nThe one thing to\ \ get right: every method answers identically. This is one handler registered for all of them, and it reads only the\ \ query string, so a body sent on the write verbs is ignored rather than refused." operationId: delete_websearch_search summary: Keyless web meta-search, in the SearXNG JSON envelope. tags: - websearch x-app: websearch x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). get: description: "Answers {query, number_of_results, results:[{url, title, content, engine}]} \u2014 the exact /search?format=json\ \ contract a SearXNG client decodes, so an agent tool configured against SearXNG reaches this with no change. `q`\ \ is the query and `language` narrows it; both are read from the QUERY STRING.\n\nServed in-process by a Go meta-search\ \ over keyless public engines, never a third-party search API and never a search key. The enabled engines run concurrently\ \ and their hits are merged, deduplicated by normalised URL (host and path, trailing slash and fragment dropped, query\ \ kept \u2014 distinct queries are distinct results) and capped at 30. Ranking is deterministic rather than scored:\ \ the first configured engine's hits lead.\n\nTWO WAYS IN, one-way equivalent, and no third: a validated principal\ \ \u2014 the same gate the whole data plane uses \u2014 passes straight through, and a caller without one must present\ \ the shared service key as X-API-Key, compared in constant time. A deployment with no key configured answers 503\ \ rather than opening the surface to everyone, and a missing or wrong key is 401. It is never an open proxy. There\ \ is no tenant scoping beyond that gate, and there is nothing to scope: the results are public web pages, identical\ \ for every caller.\n\nIt fails SOFT on the engines and closed only on the gate. An engine that errors or is served\ \ a bot-challenge page contributes zero results and never fails the request, so an empty `results` is a real answer\ \ \u2014 nothing was found \u2014 and not an outage. The array is always present, never null.\n\nThe one thing to\ \ get right: every method answers identically. This is one handler registered for all of them, and it reads only the\ \ query string, so a body sent on the write verbs is ignored rather than refused." operationId: get_websearch_search summary: Keyless web meta-search, in the SearXNG JSON envelope. tags: - websearch x-app: websearch x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). patch: description: "Answers {query, number_of_results, results:[{url, title, content, engine}]} \u2014 the exact /search?format=json\ \ contract a SearXNG client decodes, so an agent tool configured against SearXNG reaches this with no change. `q`\ \ is the query and `language` narrows it; both are read from the QUERY STRING.\n\nServed in-process by a Go meta-search\ \ over keyless public engines, never a third-party search API and never a search key. The enabled engines run concurrently\ \ and their hits are merged, deduplicated by normalised URL (host and path, trailing slash and fragment dropped, query\ \ kept \u2014 distinct queries are distinct results) and capped at 30. Ranking is deterministic rather than scored:\ \ the first configured engine's hits lead.\n\nTWO WAYS IN, one-way equivalent, and no third: a validated principal\ \ \u2014 the same gate the whole data plane uses \u2014 passes straight through, and a caller without one must present\ \ the shared service key as X-API-Key, compared in constant time. A deployment with no key configured answers 503\ \ rather than opening the surface to everyone, and a missing or wrong key is 401. It is never an open proxy. There\ \ is no tenant scoping beyond that gate, and there is nothing to scope: the results are public web pages, identical\ \ for every caller.\n\nIt fails SOFT on the engines and closed only on the gate. An engine that errors or is served\ \ a bot-challenge page contributes zero results and never fails the request, so an empty `results` is a real answer\ \ \u2014 nothing was found \u2014 and not an outage. The array is always present, never null.\n\nThe one thing to\ \ get right: every method answers identically. This is one handler registered for all of them, and it reads only the\ \ query string, so a body sent on the write verbs is ignored rather than refused." operationId: patch_websearch_search summary: Keyless web meta-search, in the SearXNG JSON envelope. tags: - websearch x-app: websearch x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). post: description: "Answers {query, number_of_results, results:[{url, title, content, engine}]} \u2014 the exact /search?format=json\ \ contract a SearXNG client decodes, so an agent tool configured against SearXNG reaches this with no change. `q`\ \ is the query and `language` narrows it; both are read from the QUERY STRING.\n\nServed in-process by a Go meta-search\ \ over keyless public engines, never a third-party search API and never a search key. The enabled engines run concurrently\ \ and their hits are merged, deduplicated by normalised URL (host and path, trailing slash and fragment dropped, query\ \ kept \u2014 distinct queries are distinct results) and capped at 30. Ranking is deterministic rather than scored:\ \ the first configured engine's hits lead.\n\nTWO WAYS IN, one-way equivalent, and no third: a validated principal\ \ \u2014 the same gate the whole data plane uses \u2014 passes straight through, and a caller without one must present\ \ the shared service key as X-API-Key, compared in constant time. A deployment with no key configured answers 503\ \ rather than opening the surface to everyone, and a missing or wrong key is 401. It is never an open proxy. There\ \ is no tenant scoping beyond that gate, and there is nothing to scope: the results are public web pages, identical\ \ for every caller.\n\nIt fails SOFT on the engines and closed only on the gate. An engine that errors or is served\ \ a bot-challenge page contributes zero results and never fails the request, so an empty `results` is a real answer\ \ \u2014 nothing was found \u2014 and not an outage. The array is always present, never null.\n\nThe one thing to\ \ get right: every method answers identically. This is one handler registered for all of them, and it reads only the\ \ query string, so a body sent on the write verbs is ignored rather than refused." operationId: post_websearch_search summary: Keyless web meta-search, in the SearXNG JSON envelope. tags: - websearch x-app: websearch x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). put: description: "Answers {query, number_of_results, results:[{url, title, content, engine}]} \u2014 the exact /search?format=json\ \ contract a SearXNG client decodes, so an agent tool configured against SearXNG reaches this with no change. `q`\ \ is the query and `language` narrows it; both are read from the QUERY STRING.\n\nServed in-process by a Go meta-search\ \ over keyless public engines, never a third-party search API and never a search key. The enabled engines run concurrently\ \ and their hits are merged, deduplicated by normalised URL (host and path, trailing slash and fragment dropped, query\ \ kept \u2014 distinct queries are distinct results) and capped at 30. Ranking is deterministic rather than scored:\ \ the first configured engine's hits lead.\n\nTWO WAYS IN, one-way equivalent, and no third: a validated principal\ \ \u2014 the same gate the whole data plane uses \u2014 passes straight through, and a caller without one must present\ \ the shared service key as X-API-Key, compared in constant time. A deployment with no key configured answers 503\ \ rather than opening the surface to everyone, and a missing or wrong key is 401. It is never an open proxy. There\ \ is no tenant scoping beyond that gate, and there is nothing to scope: the results are public web pages, identical\ \ for every caller.\n\nIt fails SOFT on the engines and closed only on the gate. An engine that errors or is served\ \ a bot-challenge page contributes zero results and never fails the request, so an empty `results` is a real answer\ \ \u2014 nothing was found \u2014 and not an outage. The array is always present, never null.\n\nThe one thing to\ \ get right: every method answers identically. This is one handler registered for all of them, and it reads only the\ \ query string, so a body sent on the write verbs is ignored rather than refused." operationId: put_websearch_search summary: Keyless web meta-search, in the SearXNG JSON envelope. tags: - websearch x-app: websearch x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/workflow: delete: description: "Answers 307 with Location /v1/workflow/ \u2014 this address serves nothing itself. The status and the\ \ Location are the same on every method; a GET additionally carries the short HTML body a browser falls back to when\ \ it does not follow the redirect itself.\n\nA 307 preserves both the method and the body, so a client that follows\ \ redirects re-sends the request unchanged to /v1/workflow/ and nothing is lost. A client that does NOT follow redirects\ \ sees only the 307 and performs no work \u2014 address /v1/workflow/ directly and the hop disappears." operationId: delete_workflow summary: Redirect to the workflow API root tags: - workflow x-app: workflow x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). get: description: "Answers 307 with Location /v1/workflow/ \u2014 this address serves nothing itself. The status and the\ \ Location are the same on every method; a GET additionally carries the short HTML body a browser falls back to when\ \ it does not follow the redirect itself.\n\nA 307 preserves both the method and the body, so a client that follows\ \ redirects re-sends the request unchanged to /v1/workflow/ and nothing is lost. A client that does NOT follow redirects\ \ sees only the 307 and performs no work \u2014 address /v1/workflow/ directly and the hop disappears." operationId: get_workflow summary: Redirect to the workflow API root tags: - workflow x-app: workflow x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). patch: description: "Answers 307 with Location /v1/workflow/ \u2014 this address serves nothing itself. The status and the\ \ Location are the same on every method; a GET additionally carries the short HTML body a browser falls back to when\ \ it does not follow the redirect itself.\n\nA 307 preserves both the method and the body, so a client that follows\ \ redirects re-sends the request unchanged to /v1/workflow/ and nothing is lost. A client that does NOT follow redirects\ \ sees only the 307 and performs no work \u2014 address /v1/workflow/ directly and the hop disappears." operationId: patch_workflow summary: Redirect to the workflow API root tags: - workflow x-app: workflow x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). post: description: "Answers 307 with Location /v1/workflow/ \u2014 this address serves nothing itself. The status and the\ \ Location are the same on every method; a GET additionally carries the short HTML body a browser falls back to when\ \ it does not follow the redirect itself.\n\nA 307 preserves both the method and the body, so a client that follows\ \ redirects re-sends the request unchanged to /v1/workflow/ and nothing is lost. A client that does NOT follow redirects\ \ sees only the 307 and performs no work \u2014 address /v1/workflow/ directly and the hop disappears." operationId: post_workflow summary: Redirect to the workflow API root tags: - workflow x-app: workflow x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). put: description: "Answers 307 with Location /v1/workflow/ \u2014 this address serves nothing itself. The status and the\ \ Location are the same on every method; a GET additionally carries the short HTML body a browser falls back to when\ \ it does not follow the redirect itself.\n\nA 307 preserves both the method and the body, so a client that follows\ \ redirects re-sends the request unchanged to /v1/workflow/ and nothing is lost. A client that does NOT follow redirects\ \ sees only the 307 and performs no work \u2014 address /v1/workflow/ directly and the hop disappears." operationId: put_workflow summary: Redirect to the workflow API root tags: - workflow x-app: workflow x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/world: get: description: "Answers GET /v1/world \u2014 the product's public endpoint, naming every wire\nthis surface answers on.\n\ \nIt exists because two of those wires are INVISIBLE to the generated document.\n/v1/world/mcp and /v1/world/zap are\ \ carved off the cloud catch-all by the\ningress and answered by world-gw, so the cloud router never serves them \u2014\ \ and\nopenapi.Describe renders prose only for a route the router actually serves,\nwhich is the very property that\ \ keeps the document from being able to claim an\noperation nothing answers. Both addresses are real and public, so\ \ without this\nop the only way to learn they exist is to read the ingress config. This is\nwhere that fact lives,\ \ in the product's own surface.\n\nPublic on purpose: discovery precedes credentials. It reports addresses and\nprotocols\ \ only \u2014 never feed data, and never the caller's plan, which\nGET /v1/world/limits owns \u2014 so there is nothing\ \ here to leak." operationId: get_world responses: '200': content: application/json: schema: $ref: '#/components/schemas/world.worldIndex' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Answers GET /v1/world \u2014 the product's public endpoint, naming every wire this surface answers on." tags: - world x-app: world x-public: true x-tool: true /v1/world/limits: get: description: "Echoes a World plan's rate limits, alert quota and model-API grant, read\nstraight from the live @hanzo/plans\ \ catalog, so agents and dashboards configure\nthemselves against the catalog instead of hardcoding tier numbers.\n\ \nAn empty or unknown plan resolves world-free, and a catalog failure serves that\nsame free floor rather than erroring\ \ \u2014 so this always answers 200, and it can only\never under-grant. It reports the contract; it does not enforce\ \ it." operationId: get_world_limits parameters: - description: "Plan is a World plan id from the live @hanzo/plans catalog, e.g. world-pro.\nEmpty means world-free,\ \ and so does an id the catalog does not know \u2014 this\nnever fails on an unknown plan." in: query name: plan required: false schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/world.limitsView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Echoes a World plan's rate limits, alert quota and model-API grant, read straight from the live @hanzo/plans catalog, so agents and dashboards configure themselves against the catalog instead of hardcoding tier numbers. tags: - world x-app: world x-public: true x-tool: true /v1/world/news: get: description: "Returns the caller's merged world-news feed: every source their project's\npipeline names \u2014 GDELT\ \ once per keyword, plus each allowlisted RSS or Atom feed \u2014\nfetched concurrently, narrowed by the pipeline's\ \ keyword/region/source filters,\ndeduplicated by link and sorted freshest first, capped at 50 items.\n\nA project\ \ with no stored pipeline gets a sensible default set of world feeds\nrather than an empty answer. A source that fails\ \ or times out is SKIPPED: the feed\ndegrades to honest partial results and never 5xxs because one outlet was down.\n\ Reading also publishes the result to the /v1/world/stream subscribers of the same\n(org, project), so a dashboard's\ \ own refresh updates every open tab." operationId: get_world_news responses: '200': content: application/json: schema: $ref: '#/components/schemas/world.newsResponse' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Returns the caller's merged world-news feed: every source their project's pipeline names \u2014 GDELT once\ \ per keyword, plus each allowlisted RSS or Atom feed \u2014 fetched concurrently, narrowed by the pipeline's keyword/region/source\ \ filters, deduplicated by link and sorted freshest first, capped at 50 items." tags: - world x-app: world x-public: true x-tool: true /v1/world/pipeline: get: description: 'Returns the caller project''s news pipeline: which feeds it reads and how the merged result is filtered. A project that has never written one is answered with the built-in world feeds and `default: true`, so a fresh project sees the same feed /v1/world/news would actually serve rather than an empty configuration.' operationId: get_world_pipeline responses: '200': content: application/json: schema: $ref: '#/components/schemas/world.pipelineView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: 'Returns the caller project''s news pipeline: which feeds it reads and how the merged result is filtered.' tags: - world x-app: world x-public: true x-tool: true put: description: "Replaces the caller project's news pipeline and returns what was\nstored. It is a WHOLE replacement, not\ \ a patch: a field the request leaves out is\nstored empty, so sending only feeds clears the filters.\n\nEvery feed\ \ URL is validated HERE, at the write boundary \u2014 http(s) only, and the\nhost must be on the server's allowlist\ \ \u2014 so a stored pipeline can never name a\nhost the fetcher would later refuse, and the allowlist is one decision\ \ in one\nplace rather than a check at each fetch." operationId: put_world_pipeline requestBody: content: application/json: schema: $ref: '#/components/schemas/world.pipelineReq' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/world.pipelineView' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Replaces the caller project's news pipeline and returns what was stored. tags: - world x-app: world x-public: true x-tool: true /v1/world/stream: get: description: "Holds the connection open as text/event-stream and pushes a `news` event \u2014 the same {items:[\u2026\ ]} body GET /v1/world/news answers \u2014 each time the caller's (org, project) feed refreshes, with a `: ping` heartbeat\ \ comment every 25s. Delivery is best-effort: a slow consumer is dropped on buffer overrun and reconnects, re-fetching\ \ GET /v1/world/news, which stays the source of truth. Requires a validated principal; 401 without one." operationId: get_world_stream summary: Live news refreshes for the caller's org and project, as Server-Sent Events. tags: - world x-app: world x-public: true responses: default: description: The route answers; its response shape is not declared at the source (not a typed op). /v1/x402/settlements: get: description: "Lists the caller's x402 receipts, newest first: as payer, what its\nledger paid; as payee, what it was\ \ paid \u2014 each settled payment with what it\nbought, both parties, the exact amount and when. A payer is the org\ \ whose ledger\nis debited, which for a SuperAdmin inspecting another org is still its own; a\npayee is the caller's\ \ own org. An unsettled claim is not a receipt and is never\nlisted." operationId: get_x402_settlements parameters: - description: "Role is payer \u2014 what the caller's org paid \u2014 or payee \u2014 what it was paid.\nPayer when\ \ empty." example: payee in: query name: role required: false schema: type: string - description: 'Year keeps the calendar year (UTC) the payments settled in. Zero keeps every year.' example: 2026 in: query name: year required: false schema: format: int64 type: integer responses: '200': content: application/json: schema: $ref: '#/components/schemas/x402.settlementList' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: "Lists the caller's x402 receipts, newest first: as payer, what its ledger paid; as payee, what it was paid\ \ \u2014 each settled payment with what it bought, both parties, the exact amount and when." tags: - x402 x-app: x402 x-public: true x-tool: true /v1/x402/settlements/{id}: get: description: "Reads one x402 payment receipt by id.\n\nIt is scoped to the caller's PAYER org \u2014 the ledger that\ \ was debited \u2014 so one\ntenant can never read another's settlement, and an id that exists but belongs\nto somebody\ \ else is a 404 exactly like one that does not exist. A caller with\nno billable identity is refused outright." operationId: get_x402_settlements_by_id parameters: - description: "ID is the settlement id from the URL \u2014 the deterministic keccak(from|nonce)\nkey an x402 receipt\ \ is issued under (the `id` field of a Receipt, and the\n`transaction` of the SettlementResponse on the PAYMENT-RESPONSE\ \ header a paid\nrequest answers with)." in: path name: id required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/x402.Receipt' description: ok default: content: application/problem+json: schema: $ref: '#/components/schemas/problem-details' description: refused summary: Reads one x402 payment receipt by id. tags: - x402 x-app: x402 x-public: true x-tool: true /v1/{name}: get: description: "What the capability named in the path answers: every published operation, its operationId, its method\ \ and its address, and the sentence lifted from the handler that serves it \u2014 plus the way back to /v1.\n\nIt\ \ answers where the capability serves nothing at its own root. Where it does, that operation is the answer and is\ \ described at its own address; a client following the root index reaches the capability either way.\n\nUnauthenticated,\ \ and scoped to the same customer surface the root is. A name that is not a published capability is answered exactly\ \ as any other unrouted address, so this cannot be asked whether something exists that it would not have listed." operationId: get_capability parameters: - in: path name: name required: true schema: type: string responses: 2XX: content: application/json: schema: $ref: '#/components/schemas/Index' description: Success security: [] summary: One capability's operations, with the address and method to call each tags: - openapi x-app: openapi x-public: true security: - bearer: [] servers: - url: https://api.hanzo.ai tags: - name: iam description: 'Package iam is Hanzo''s identity provider: users, organizations, applications, and the OIDC/OAuth2 endpoints every Hanzo service authenticates against.' - name: authz description: 'Package serve is the access decision behind an org-scoped call: who may do what, where, answered allow or deny.' - name: kms description: 'Package kms is secret custody: your org''s secrets sealed at rest, plus threshold signing.' - name: security description: 'Package security is secret scanning for your code: submit sources, get findings, masked never raw.' - name: team description: 'Package team is your org''s shared space: documents edited together, files, seats, and agents as teammates.' - name: audit description: 'Package auditlog is your org''s tamper-evident audit trail: every security-relevant event, hash-chained and readable.' - name: compliance description: Package compliance is your KYC/KYB onboarding, accreditation records, and the evidence trail behind them. - name: risk description: 'Package risk is HANZO RISK''s model plane: the per-organisation feature surface and the per-organisation models trained on it.' - name: validator description: 'Package validators is one-click validator onboarding: prove your Genesis NFT, get a node provisioned, queue its registration.' - name: wallet description: 'Package wallets is blockchain key custody: create wallets, rotate their keys, and sign with them.' - name: entitlement description: 'Package entitlements is what your org may run: what the plan grants, and which of those products are switched on.' - name: account description: 'Package account is your own account: API keys you mint and revoke, and org onboarding.' - name: dataset description: 'Package dataset is the per-org dataset plane of /v1/risk: a dataset is a VERSIONED, IMMUTABLE snapshot of one tenant''s own event surface, and this is where it is declared, materialised, described, exported and disposed of.' - name: label description: 'Package label is the ground-truth plane: what actually turned out to be fraud, who said so, and when they could first have said it.' - name: reference description: 'Package reference is the lookup data a risk decision needs but cannot derive: which email domains hand out throwaway inboxes, which addresses belong to a datacentre or a Tor exit, which card scheme an issuer prefix belongs to, which browsers the fleet sees everywhere, and how current the designation lists the screening engine holds actually are.' - name: trust description: 'Package trust is your trust centre: the controls you publish, the coverage they compute to, the documents a reviewer asks for, and who you send data to.' - name: principal description: 'Package principals serves an org as an economic principal: who it is, what it may pay, and which agents answer to it.' - name: ai description: "Package ai is Hanzo AI \u2014 the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the\ \ rest of hanzoai/ai's surface) \u2014 mounted into a cloud binary with the money, ingest and telemetry callbacks cloud\ \ BUILDS but cannot INSTALL." - name: agent description: 'Package agents is autonomous agents for your org: define them, run them, keep every run.' - name: ml description: 'Package ml is model serving: deploy a model behind an endpoint and call it.' - name: eval description: Package eval is scoring a model on your own data, with a judge you choose. - name: benchmark description: Package benchmark is one honest score for any model, on the tests everyone quotes. - name: prompt description: Package prompts is your prompt library, versioned, so nothing changes silently. - name: exec description: 'Package exec is the code interpreter: run a snippet in a sandbox, and move files in and out of the session that sandbox IS.' - name: sandbox description: 'Package sandbox is the ONE compute primitive: a sandbox is a gVisor pod that runs somebody else''s code, and every lifetime is the same object.' - name: tool description: 'Package tools is everything your org can call, in one list: connector actions, functions, agents, skills and your own MCP servers.' - name: ask description: Package ask is a plain-language question about your business, answered with real numbers. - name: code description: Package code is search and symbols across your repos, for you and your agents. - name: engine description: 'Package engine is Hanzo Engine: which models the serving runtime has loaded, and the GPUs under it.' - name: translate description: Package translate is text in, the same text out in the language you asked for. - name: lsp description: "Package lsp is live semantic code intelligence \u2014 definitions, references, types, hover, outline and diagnostics\ \ \u2014 over a repository AND its resolved dependencies, with no toolchain on the caller's machine." - name: s3 description: 'Package s3 is object storage: your buckets and the files in them, with signed URLs for upload and download.' - name: space description: 'Package space is where work lives: drives, folders and the files in them.' - name: base description: "Package base is managed Hanzo Base: a hosted backend for your app \u2014 collections, records, access rules\ \ and sign-in." - name: kv description: 'Package kv is your key-value store: buckets of versioned values your apps read and write by key.' - name: search description: Package search is one ranked result set over everything your org has stored. - name: index description: Package index is fast full-text search over your own data, typos forgiven. - name: catalog description: Package catalog is one place to browse every project, app and site built here. - name: taxonomy description: 'Package taxonomy is the product catalogue''s shape: which categories exist, what each product is called, which category it sits in, what it is tagged with, and the order the two are shown in.' - name: sync description: 'Package sync is data sync: link two endpoints and keep them in step, on a webhook, on a schedule, or on demand.' - name: crawl description: Package crawl is any web page turned into clean markdown a model can read. - name: websearch description: Package websearch is a web search and a page fetch your agents can call. - name: provisioning description: 'Package provisioning is one-click data add-ons: a SQL, key-value, document, vector, search or object store, wired straight into your app.' - name: knowledge description: Package knowledge is your team's wiki and your agents' memory, searchable by meaning. - name: graph description: 'Package graph is one organization''s entities and the relations between them, held as assertions: somebody, at some moment, from some evidence, asserted that this thing stands in that relation to that other thing.' - name: pubsub description: 'Package pubsub is your message bus: publish, subscribe, and durable streams your apps read at their own pace.' - name: mq description: 'Package mq is queue and stream admin for your org: create them, watch them drain, ack what you pulled.' - name: notify description: Package notify is transactional email and SMS, sent through your org's own provider credential. - name: tel description: 'Package tel is the telecommunications surface: phone numbers, calls and messages, on whatever carrier the deployment is configured for.' - name: auto description: Package auto is workflows that run themselves, on a schedule or a webhook. - name: flow description: 'Package flow is Hanzo Flow: build an agent workflow on a visual canvas, run it, and read every run.' - name: webhook description: 'Package webhooks is how your app hears about events: register an endpoint, pick the events, get each one delivered and signed.' - name: event description: 'Package analytics is product analytics: send an event, read back who did what.' - name: channel description: "Package channels is one inbox for the chat apps you connect \u2014 Discord, Slack, Teams, Telegram." - name: destination description: Package destinations is your events forwarded to the ad and analytics tools you use. - name: provider description: Package integrations is how your org connects third-party accounts like Slack, and revokes them. - name: workflow description: 'Package workflow is Hanzo Workflow: durable workflows that survive a crash, with every run visible and replayable.' - name: o11y description: 'Package o11y is your logs, metrics and traces: ship them in, query them, chart them.' - name: metrics - name: usage description: Package usage is what your org ran and what it cost, broken down per account. - name: openapi description: 'The served contract: the OpenAPI document every client, tool list and command group is generated from, its command projection, the agent endpoint that offers the same operations over MCP, and the index a client follows from the API root to reach any of it.' - name: leaderboard description: Package leaderboard is the ranking of who uses AI most, in your org and globally. - name: commerce description: 'Package commerce is selling: checkout, subscriptions, invoices, spend alerts, payment webhooks and the storefront catalog.' - name: marketplace description: 'Package marketplace is the one market between orgs: agents and personas to hire, apps, skills, MCP servers and tools to buy, each sold by the org that owns it, and the two-sided work one org buys from another.' - name: billing description: Package billing is your org's balance, what it has spent, and the cards it pays with. - name: pricing description: 'Package pricing is the price list: what every model, provider, GPU tier, tool and hosting plan costs.' - name: books description: 'Package books is double-entry accounting: chart of accounts, ledger, bank reconciliation, and the reports that prove the books balance.' - name: captable description: 'Package captable is your cap table: stakeholders, share classes, grants, SAFEs, rounds, and who owns what.' - name: referral description: 'Package referrals is referral ATTRIBUTION: who referred whom, and whether that referee ever became a real customer.' - name: affiliate description: Package affiliates is a partner program that pays commission on what your referrals spend. - name: author description: 'Package authors is a royalty for open-source work: your repo runs, you get paid.' - name: licensing description: 'Package licensing is what an org has bought and may run: signed licenses to issue, verify and revoke, and the releases they unlock.' - name: x402 description: 'Package x402 is pay-per-request over HTTP 402: quote a price, take the payment, serve the resource.' - name: marketing description: 'Package marketing is lifecycle email: drip sequences that reach the right people.' - name: campaign description: Package campaign is one go-to-market push across paid, organic and email at once. - name: ad description: Package ads is your paid ad campaigns, launched and paused from one place. - name: experiment description: 'Package experiments is A/B testing anything: a flag, an ad, a subject line, a model.' - name: treasury description: 'Package treasury is the reserve fund behind every payout: real capital, held and accounted for.' - name: plan description: 'Package plan is the plan catalog: every tier you can buy, what it costs, and what it grants.' - name: allowance description: Package allowance is how much a plan lets you do without paying, and how much of it you have left today. - name: platform description: "Package platform is Hanzo PaaS: deploy containers to your own tenant namespace \u2014 builds, releases, environments,\ \ logs, custom domains." - name: deploy description: 'Package deploy is Hanzo CD: see what each app is running, sync it, and roll back a bad release.' - name: project description: 'Package projects is where your sites live: create one, deploy a build, roll back to any release.' - name: ci description: Package ci mounts the CI fleet dashboard as a capability of this binary. - name: registry description: 'Package registry is your container and package registry: push images, pull them back, see what you store.' - name: gateway description: 'Package gateway is live control of the policy your API applies to every incoming request: CORS, rate limits, cache TTL and allowed methods, changed without a redeploy.' - name: ingress description: 'Package ingress is your edge: automatic TLS certificates and hostname routing to any backend, changed live.' - name: dns description: 'Package dns is your DNS records: the zones and records behind every name you point at Hanzo.' - name: domain description: 'Package domain is Hanzo Domains: search a name, see the price, buy it from your prepaid wallet.' - name: cloudflare description: 'Package cloudflare is your Cloudflare account, managed from Hanzo: zones, Pages, Workers, Workers AI, R2, KV and D1.' - name: network description: "Package network mounts the Hanzo Cloud NETWORKING surface: the tenant's Hanzo Zero Trust footprint \u2014\ \ the overlay network, its routers and its services \u2014 served as clean, org-scoped REST off the unified cloud binary\ \ and fronting the Hanzo Zero Trust controller (hanzoai/zt, an ZT-based fabric)." - name: flag description: 'Package flags is feature flags: ship it dark, then turn it on for who you pick.' - name: compute description: "Package compute is the compute you rent from Hanzo: machines, GPUs and clusters \u2014 launch one, resize\ \ it, tear it down." - name: node description: 'Package nodes is your machines: the ones you own, connected and ready to take a command.' - name: environment description: 'Package environment is what a codebase is before its agent starts: the commands that install it, the command that starts it, and the secrets it reads.' - name: git description: 'Package git is Git hosting for your org: create repos, clone, push, and see what they cost.' - name: template description: Package templates is a gallery of starter kits you can deploy as they come. - name: blueprint description: Package blueprint is what a template costs to run, worked out before you deploy. - name: bot description: Package bot is a bot doing your work on a real desktop, live, while you watch. - name: world description: Package world is a live news feed filtered to what your project cares about. - name: framework description: 'Package framework is document types you define: describe a record once, then create, list, submit and cancel documents against it.' - name: content description: Package content is marketing content from draft to published, on every channel. - name: guide description: Package guide is the AI that builds an agentic company with you, one measured step at a time. - name: help description: 'Package help is a support desk: customers file tickets, your team answers them.' - name: meet description: 'Package meet is the virtual office: it decides who may join a room and mints the short-lived token that lets them in.' - name: share description: Package share is a public URL for a service on your own machine, and a list of what you have open. - name: social description: Package social is posting to every social account you own, now or on a schedule. - name: esign description: Package esign is a document out for signature, signed and filed with an audit trail. - name: dataroom description: Package dataroom is a secure document room you share by link and watch page by page. - name: company description: 'Package company is incorporation end to end: pick a structure, add founders, pay, file, and e-sign.' - name: legal description: Package legal is the paperwork your company needs, drafted, signed and filed. - name: pref description: "Package prefs is your own settings \u2014 theme, density, pinned nav \u2014 following you across every Hanzo\ \ app." - name: settings description: Package settings is how an org configures each product it uses, secret fields included. - name: function description: 'Package functions is your serverless code: publish it, call it over HTTP, watch every run and what it cost.' - name: sbom description: 'Package sbom is what is inside a container image: every component, resolvable by digest or image ref.' - name: link description: 'Package link is the unified AI login manager''s registry: the org+user-scoped record of WHICH provider accounts (Claude Max, ChatGPT Plus, a Hanzo API key, a raw provider key) a developer has signed into, ON WHICH MACHINES, with each account''s latest usage snapshot.' - name: seo description: 'Package seo is search visibility as data: what a phrase is worth, what a site already places for, who places beside it, who links to it, and what one page gets wrong.' - name: standing description: Package standing prices and tracks what it costs to KEEP a company, as distinct from what it cost to form one. - name: task description: 'Package task is Hanzo Task: boards, the work items on them, and the filters that make a board.' - name: tax description: Package tax is how companies formed on Hanzo exchange W-9s, W-8s and 1099s with each other, from the payments that moved between them on Hanzo's rails. - name: patrol description: 'Package patrol stands watch over an estate: sites, units, tours, keys, cameras, and the incidents that interrupt them.' - name: web3 description: 'Package web3 is the chain-access surface: which chains this deployment can reach, a JSON-RPC endpoint onto each, and the two token reads every wallet UI needs.' - name: explorer description: 'Package explorer is chain data: your block indexers and how far each has caught up, plus the on-chain price feeds.' - name: market description: Package market is what trades on each chain, and how far the read of it got. x-tagGroups: - name: Identity & Trust tags: - iam - authz - kms - security - team - audit - compliance - risk - validator - wallet - entitlement - account - dataset - label - reference - trust - principal - name: Intelligence tags: - ai - agent - ml - eval - benchmark - prompt - exec - sandbox - tool - ask - code - engine - translate - lsp - name: Data tags: - s3 - space - base - kv - search - index - catalog - taxonomy - sync - crawl - websearch - provisioning - knowledge - graph - name: Streams tags: - pubsub - mq - notify - tel - auto - flow - webhook - event - channel - destination - provider - workflow - name: Observability tags: - o11y - metrics - usage - openapi - leaderboard - name: Commerce tags: - commerce - marketplace - billing - pricing - books - captable - referral - affiliate - author - licensing - x402 - marketing - campaign - ad - experiment - treasury - plan - allowance - name: Platform tags: - platform - deploy - project - ci - registry - gateway - ingress - dns - domain - cloudflare - network - flag - compute - node - environment - name: Applications tags: - git - template - blueprint - bot - world - framework - content - guide - help - meet - share - social - esign - dataroom - company - legal - pref - settings - function - sbom - link - seo - standing - task - tax - patrol - name: Chain tags: - web3 - explorer - market