openapi: 3.1.0 info: title: Samyama Graph Database API version: 1.7.1 description: | HTTP API for the Samyama high-performance distributed graph database. Supports OpenCypher queries, graph status, and CRUD operations. license: name: Apache-2.0 url: https://www.apache.org/licenses/LICENSE-2.0 servers: - url: http://localhost:8080 description: Local development server paths: /api/query: post: operationId: executeQuery summary: Execute a Cypher query description: | Execute an OpenCypher query against the graph database. Supports both read (MATCH) and write (CREATE, SET, DELETE, MERGE) queries. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/QueryRequest' examples: matchAll: summary: Match all Person nodes value: query: "MATCH (n:Person) RETURN n" matchWithFilter: summary: Match with WHERE filter value: query: "MATCH (n:Person) WHERE n.age > 25 RETURN n.name, n.age" createNode: summary: Create a new node value: query: 'CREATE (n:Person {name: "Alice", age: 30})' traversal: summary: Traverse relationships value: query: "MATCH (a:Person)-[:KNOWS]->(b:Person) RETURN a.name, b.name" responses: '200': description: Query executed successfully content: application/json: schema: $ref: '#/components/schemas/QueryResponse' '400': description: Query error (parse error, execution error) content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/schema: get: operationId: getSchema summary: Get graph schema description: | Returns the full schema of the graph database including node types with property keys/types, edge types with source/target labels, indexes, constraints, and basic statistics. responses: '200': description: Graph schema content: application/json: schema: $ref: '#/components/schemas/SchemaResponse' /api/import/csv: post: operationId: importCsv summary: Import nodes from CSV description: | Upload a CSV file to create nodes. The first row is treated as column headers (property names). Each subsequent row creates one node with the specified label. Property types are auto-detected (integer → float → boolean → string). requestBody: required: true content: multipart/form-data: schema: type: object required: - file - label properties: file: type: string description: CSV file content label: type: string description: Node label to assign to all created nodes id_column: type: string description: Column to use as node ID mapping (optional) delimiter: type: string description: "CSV delimiter character (default: ',')" graph: type: string description: "Target graph/tenant name (default: 'default')" responses: '200': description: Import successful content: application/json: schema: $ref: '#/components/schemas/CsvImportResponse' '400': description: Import error (missing fields, parse error) content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/import/json: post: operationId: importJson summary: Import nodes from JSON description: | Create nodes from an array of JSON objects. Each object becomes one node with the specified label. Object keys become property names; values must be string, number, or boolean (other types are silently skipped). requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/JsonImportRequest' example: label: Person nodes: - name: Alice age: 30 - name: Bob age: 25 responses: '200': description: Import successful content: application/json: schema: $ref: '#/components/schemas/JsonImportResponse' '400': description: Import error (missing label, invalid data) content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/status: get: operationId: getStatus summary: Get server status description: Returns the health status, version, and storage statistics of the database. responses: '200': description: Server status content: application/json: schema: $ref: '#/components/schemas/StatusResponse' example: status: healthy version: 1.7.1 storage: nodes: 2000 edges: 11000 /api/tenants: get: operationId: listTenants summary: List all tenants description: Returns all configured tenants with their settings and quotas. responses: '200': description: List of tenants content: application/json: schema: type: object properties: tenants: type: array items: $ref: '#/components/schemas/Tenant' post: operationId: createTenant summary: Create a new tenant description: Create a new tenant with optional resource quotas. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateTenantRequest' example: id: analytics name: Analytics Team responses: '201': description: Tenant created content: application/json: schema: $ref: '#/components/schemas/Tenant' '409': description: Tenant already exists content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/tenants/{id}: parameters: - name: id in: path required: true schema: type: string description: Tenant ID get: operationId: getTenant summary: Get a tenant description: Returns a single tenant by ID. responses: '200': description: Tenant details content: application/json: schema: $ref: '#/components/schemas/Tenant' '404': description: Tenant not found content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' delete: operationId: deleteTenant summary: Delete a tenant description: Delete a tenant by ID. The "default" tenant cannot be deleted. responses: '200': description: Tenant deleted content: application/json: schema: type: object properties: status: type: string id: type: string '403': description: Cannot delete default tenant content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '404': description: Tenant not found content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' patch: operationId: updateTenant summary: Update tenant settings description: | Update a tenant's enabled status, resource quotas, or configuration. Only provided fields are updated; omitted fields remain unchanged. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateTenantRequest' responses: '200': description: Updated tenant content: application/json: schema: $ref: '#/components/schemas/Tenant' '404': description: Tenant not found content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/sample: post: operationId: sampleSubgraph summary: Sample a representative subgraph description: > Returns a label-stratified, size-capped subgraph intended for visualization. One call replaces the `1 + ` sequential queries a client would otherwise issue to draw an overview, and the default of 200 nodes is the point past which a node-link diagram stops being legible. requestBody: required: false content: application/json: schema: type: object properties: max_nodes: type: integer default: 200 description: Upper bound on returned nodes. Capped server-side at 1000. labels: type: array items: type: string description: Restrict sampling to these labels. Empty means all labels. graph: type: string description: Tenant/graph name. responses: '200': description: A sampled subgraph content: application/json: schema: type: object /api/vector/indexes: get: operationId: listVectorIndexes summary: List vector indexes responses: '200': description: The configured vector indexes content: application/json: schema: type: object post: operationId: createVectorIndex summary: Create a vector index requestBody: required: true content: application/json: schema: type: object required: - label - property_key - dimensions properties: label: type: string property_key: type: string dimensions: type: integer metric: type: string description: Distance metric, e.g. cosine or euclidean. responses: '200': description: The index was created /api/vector-search: post: operationId: vectorSearch summary: k-nearest-neighbour search over a vector index description: > Supply either `query_vector` or `query_text`; supplying text requires an embedding provider to be configured. requestBody: required: true content: application/json: schema: type: object properties: query_text: type: string query_vector: type: array items: type: number format: float label: type: string property_key: type: string k: type: integer description: Number of neighbours to return. responses: '200': description: The nearest neighbours, closest first content: application/json: schema: type: object /api/snapshot/export: post: operationId: exportSnapshot summary: Export a tenant as a portable .sgsnap snapshot responses: '200': description: The snapshot bytes content: application/octet-stream: schema: type: string format: binary /api/snapshot/import: post: operationId: importSnapshot summary: Import a .sgsnap snapshot into a tenant description: > The body is **multipart/form-data**, not a raw octet stream — posting the bytes directly is the mistake this description exists to prevent. Import merges into the target tenant rather than replacing it. requestBody: required: true content: multipart/form-data: schema: type: object properties: file: type: string format: binary responses: '200': description: The snapshot was imported /api/enrich/policy: post: operationId: setEnrichPolicy summary: Declare the governed enrichment (GAK) policy description: | Declare the Generation-Augmented Knowledge (GAK) policy: which node properties the LLM may fill, the trust floor each must clear for promotion, and any subgraph (edge) materialisation specs. Declared but unfilled properties become the gaps that `/api/enrich` detects. requestBody: required: true content: application/json: schema: type: object description: > The governed policy. `specs` lists per-label property gaps to fill; a `materialize` block turns a filled list into real nodes and edges. examples: scalarAndSubgraph: summary: A scalar property plus a MONITORS-edge subgraph spec value: specs: - label: "Sensor" property: "monitored_failure_modes" trust_floor: 0.6 materialize: edge_type: "MONITORS" target_label: "FailureMode" target_key: "name" responses: '200': description: Policy accepted content: application/json: schema: type: object properties: status: type: string declared_properties: type: integer /api/enrich: post: operationId: enrich summary: Run governed enrichment over a query's surfaced nodes description: | Execute a Cypher query, detect declared-but-null property gaps on the surfaced nodes, fill each with a single LLM call, and quarantine the result under `_enrichment` with provenance. Honest-decline: an UNKNOWN answer writes nothing. Promotion into the live property happens only at `/api/verify`. requestBody: required: true content: application/json: schema: type: object required: - query properties: query: type: string description: The Cypher query whose surfaced nodes are enriched. responses: '200': description: Enrichment completed content: application/json: schema: type: object properties: gaps: type: integer filled: type: integer declined: type: integer '400': description: Query or enrichment error content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/verify: post: operationId: verifyEnrichment summary: Promote quarantined enrichments that clear the trust floor description: | Walk quarantined `_enrichment` values, promote each whose confidence meets the policy trust floor into the live property, and materialise any declared subgraph edges (for example MONITORS edges to FailureMode nodes). Values below the floor stay pending. responses: '200': description: Verification completed content: application/json: schema: type: object properties: nodes_processed: type: integer promoted: type: integer edges_materialized: type: integer still_pending: type: integer /api/nlq: post: operationId: naturalLanguageQuery summary: Translate a natural-language question into read-only Cypher description: | Translate a natural-language question into a read-only OpenCypher query using a configured LLM provider. NLQ must be enabled via environment (`NLQ_MODEL`, `OPENAI_API_KEY`, optional `NLQ_API_BASE_URL`). Returns the generated Cypher only; execute it with `/api/query`. requestBody: required: true content: application/json: schema: type: object required: - question properties: question: type: string description: The natural-language question to translate. examples: ask: summary: Ask for equipment by work-order count value: question: "Which chillers have more than 100 work orders?" responses: '200': description: Cypher generated successfully content: application/json: schema: type: object properties: cypher: type: string '400': description: NLQ disabled or generation error content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' components: schemas: QueryRequest: type: object required: - query properties: query: type: string description: An OpenCypher query string examples: - "MATCH (n:Person) RETURN n" graph: type: string description: "Target graph/tenant name (default: 'default')" default: default QueryResponse: type: object properties: nodes: type: array description: Graph nodes referenced in the result (for visualization) items: $ref: '#/components/schemas/GraphNode' edges: type: array description: Graph edges referenced in the result (for visualization) items: $ref: '#/components/schemas/GraphEdge' columns: type: array description: Column names for the tabular result items: type: string records: type: array description: Tabular result rows, one array per record items: type: array items: {} GraphNode: type: object properties: id: type: string description: Node ID labels: type: array items: type: string description: Node labels properties: type: object additionalProperties: true description: Node properties GraphEdge: type: object properties: id: type: string description: Edge ID source: type: string description: Source node ID target: type: string description: Target node ID type: type: string description: Relationship type properties: type: object additionalProperties: true description: Edge properties StatusResponse: type: object properties: status: type: string description: Server health status examples: - healthy version: type: string description: Server version examples: - 1.7.1 storage: type: object properties: nodes: type: integer description: Number of nodes in the graph edges: type: integer description: Number of edges in the graph SchemaResponse: type: object properties: node_types: type: array items: type: object properties: label: type: string count: type: integer properties: type: object additionalProperties: type: string description: "Property type: String, Integer, Float, Boolean, Vector, Unknown" edge_types: type: array items: type: object properties: type: type: string count: type: integer source_labels: type: array items: type: string target_labels: type: array items: type: string properties: type: object additionalProperties: type: string indexes: type: array items: type: object properties: label: type: string property: type: string type: type: string constraints: type: array items: type: object properties: label: type: string property: type: string type: type: string statistics: type: object properties: total_nodes: type: integer total_edges: type: integer avg_out_degree: type: number JsonImportRequest: type: object required: - label - nodes properties: label: type: string description: Node label to assign to all created nodes nodes: type: array description: Array of JSON objects, each becoming a node items: type: object additionalProperties: true graph: type: string description: "Target graph/tenant name (default: 'default')" default: default CsvImportResponse: type: object properties: status: type: string examples: - ok nodes_created: type: integer label: type: string columns: type: array items: type: string JsonImportResponse: type: object properties: status: type: string examples: - ok nodes_created: type: integer label: type: string ErrorResponse: type: object properties: error: type: string description: Error message Tenant: type: object properties: id: type: string description: Tenant ID (unique identifier) name: type: string description: Display name enabled: type: boolean description: Whether the tenant is enabled created_at: type: integer description: Creation timestamp (Unix seconds) quotas: $ref: '#/components/schemas/ResourceQuotas' nlq_config: type: object nullable: true description: NLQ configuration agent_config: type: object nullable: true description: Agent configuration embed_config: type: object nullable: true description: Auto-embed configuration ResourceQuotas: type: object properties: max_nodes: type: integer nullable: true description: Maximum number of nodes max_edges: type: integer nullable: true description: Maximum number of edges max_memory_bytes: type: integer nullable: true description: Maximum memory in bytes max_storage_bytes: type: integer nullable: true description: Maximum storage in bytes max_connections: type: integer nullable: true description: Maximum concurrent connections max_query_time_ms: type: integer nullable: true description: Maximum query execution time in milliseconds CreateTenantRequest: type: object required: - id - name properties: id: type: string description: Tenant ID (unique) name: type: string description: Display name quotas: $ref: '#/components/schemas/ResourceQuotas' UpdateTenantRequest: type: object properties: enabled: type: boolean description: Enable or disable the tenant quotas: $ref: '#/components/schemas/ResourceQuotas' nlq_config: type: object description: NLQ configuration agent_config: type: object description: Agent configuration embed_config: type: object description: Auto-embed configuration UsageResponse: type: object properties: tenant_id: type: string node_count: type: integer edge_count: type: integer memory_bytes: type: integer storage_bytes: type: integer active_connections: type: integer