{"openapi":"3.0.0","info":{"title":"Purple Suite Cloud Logs API","version":"1.0.0","description":"Read-only synthetic cloud inventory and application observations. Correlate project, stable resource identity, FQDN and observation time with your inventory source. Does not modify cloud infrastructure or CMDB relationships.\n\n---\n\n**Acting as a user (`X-PS-Impersonate-User`).** Every endpoint accepts an optional `X-PS-Impersonate-User` request header whose value is the email of the instance user to act as. The effective user resolves in this order: the header if present, else the instance's configured default MCP user, else none. User-scoped paths additionally accept the literal `me` in place of an email, and Graph-style apps also serve `/me/…` twins of their `/users/{email}/…` paths that resolve the same way. This header is honored by all operations but is intentionally omitted from each operation's parameter list so it is not surfaced as a per-tool argument by MCP clients; see the `ImpersonateUser` entry under `components.parameters`."},"servers":[{"url":"https://marketplace.moveworks.com/api/purple-suite/cloud-logs"}],"security":[{"patAuth":[]}],"components":{"securitySchemes":{"patAuth":{"type":"http","scheme":"bearer"}},"parameters":{"InstanceId":{"in":"header","name":"X-Instance-ID","required":true,"schema":{"type":"string"}},"ImpersonateUser":{"in":"header","name":"X-PS-Impersonate-User","required":false,"schema":{"type":"string","format":"email"},"description":"Optional. Act as a specific user in the target instance — the value is that user's email (must exist in the instance). Resolution order for the effective user: (1) this header, if set; (2) the instance's configured default MCP user; (3) otherwise no user is bound. User-scoped paths (e.g. /users/{email}/…) also accept the literal \"me\", and Graph-style apps serve /me/… twins of those paths (Graph: /me ≡ /users/{signed-in-user}) — both resolve to whatever this header (or the default MCP user) points at. An explicit path email always wins over the header. The unified MCP server does not send this header — it relies on the per-instance default — so it is primarily for direct REST/HTTP callers."}},"schemas":{"CloudResource":{"type":"object","required":["id","name","resourceUid","projectId","resourceKind","validFrom","validTo"],"properties":{"id":{"type":"string","readOnly":true},"instanceId":{"type":"string","readOnly":true,"description":"Purple Suite tenant ID, not a cloud compute instance ID."},"synthetic":{"type":"boolean","readOnly":true},"snapshotAt":{"type":"string","format":"date-time","description":"Fixed historical observation snapshot; these records are not live cloud data."},"name":{"type":"string"},"fqdn":{"type":"string"},"ipAddress":{"type":"string"},"projectId":{"type":"string"},"resourceUid":{"type":"string","description":"Stable cloud resource identity; not a CMDB sys_id."},"podUid":{"type":"string"},"cloudInstanceId":{"type":"string","description":"Compute instance identifier as a string to preserve 64-bit precision. Not the Purple Suite tenant ID."},"resourceKind":{"type":"string"},"environment":{"type":"string"},"region":{"type":"string"},"zone":{"type":"string"},"resourceUri":{"type":"string","description":"Fictional inventory URI; not a callable cloud endpoint."},"status":{"type":"string","enum":["active","retired"]},"validFrom":{"type":"string","format":"date-time"},"validTo":{"type":"string","format":"date-time","nullable":true},"lastObservedAt":{"type":"string","format":"date-time"},"clusterName":{"type":"string"},"namespace":{"type":"string"},"kubernetesUid":{"type":"string"},"hostingNodeName":{"type":"string"},"hostingInstanceId":{"type":"string"},"workloadName":{"type":"string"},"nodePoolName":{"type":"string"},"machineType":{"type":"string"},"osVersion":{"type":"string"},"nodeVersion":{"type":"string"},"endpointPort":{"type":"integer"},"engine":{"type":"string"},"engineVersion":{"type":"string"}}},"PeerIdentity":{"type":"object","properties":{"name":{"type":"string"},"fqdn":{"type":"string"},"ipAddress":{"type":"string"},"projectId":{"type":"string"},"resourceUid":{"type":"string","description":"Stable cloud resource identity; not a CMDB sys_id."},"podUid":{"type":"string"},"cloudInstanceId":{"type":"string","description":"Compute instance identifier as a string to preserve 64-bit precision. Not the Purple Suite tenant ID."}}},"CloudLogEntry":{"type":"object","required":["id","insertId","timestamp","projectId","resource","jsonPayload"],"properties":{"id":{"type":"string","readOnly":true},"instanceId":{"type":"string","readOnly":true,"description":"Purple Suite tenant ID, not a cloud compute instance ID."},"synthetic":{"type":"boolean","readOnly":true},"snapshotAt":{"type":"string","format":"date-time","description":"Fixed historical observation snapshot; these records are not live cloud data."},"insertId":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"severity":{"type":"string","enum":["INFO","WARNING"]},"logName":{"type":"string"},"projectId":{"type":"string"},"environment":{"type":"string"},"serverName":{"type":"string","description":"Hosting node of the emitting pod, or the emitting service name for non-pod logs."},"podName":{"type":"string"},"podUid":{"type":"string"},"resource":{"type":"object","properties":{"type":{"type":"string"},"labels":{"type":"object","additionalProperties":{"type":"string"}}}},"jsonPayload":{"type":"object","properties":{"eventType":{"type":"string","enum":["request","health_check","connection_attempt"]},"direction":{"type":"string","enum":["inbound","outbound"],"description":"Direction relative to the emitting resource. Source is always the caller; destination is always the callee."},"source":{"$ref":"#/components/schemas/PeerIdentity"},"destination":{"$ref":"#/components/schemas/PeerIdentity"},"protocol":{"type":"string","enum":["HTTPS","HTTP","Redis","PostgreSQL"]},"destinationPort":{"type":"integer"},"operation":{"type":"string"},"status":{"type":"string","enum":["OK","TIMEOUT","CONNECTION_REFUSED"]},"latencyMs":{"type":"integer"}}}}}}},"paths":{"/resources":{"get":{"summary":"Search cloud inventory","description":"Find cloud resources by name, project, resource identity, address, kind, or hosting node. Returns runtime identity, hosting metadata and validity windows; names and IPs need not be unique. Supports OData $filter/$select/$orderby/$search/$top/$skip/$count (MCP uses _filter, _top, etc.). Use $orderby with an id tie-breaker and $top/$skip for stable paging. Nested fields use slash paths. This is a Purple Suite mock API, not a native cloud provider API contract.","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"query","name":"limit","schema":{"type":"integer","default":50},"description":"Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used."},{"in":"query","name":"cursor","schema":{"type":"string"},"description":"Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top."},{"in":"query","name":"$filter","schema":{"type":"string"},"description":"OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."},{"in":"query","name":"$select","schema":{"type":"string"},"description":"Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."},{"in":"query","name":"$orderby","schema":{"type":"string"},"description":"Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."},{"in":"query","name":"$search","schema":{"type":"string"},"description":"Free-text, case-insensitive substring match across the resource's searchable fields."},{"in":"query","name":"$top","schema":{"type":"integer"},"description":"Maximum number of items to return (page size)."},{"in":"query","name":"$skip","schema":{"type":"integer"},"description":"Number of matching items to skip before returning results (offset paging). Combine with $top."},{"in":"query","name":"$count","schema":{"type":"boolean"},"description":"When true, include @odata.count (total matches after $filter/$search, before paging) in the response."},{"in":"query","name":"projectId","schema":{"type":"string"},"description":"Exact-match projectId filter. Combine with $filter for other predicates."},{"in":"query","name":"environment","schema":{"type":"string"},"description":"Exact-match environment filter. Combine with $filter for other predicates."},{"in":"query","name":"name","schema":{"type":"string"},"description":"Exact-match name filter. Combine with $filter for other predicates."},{"in":"query","name":"resourceKind","schema":{"type":"string"},"description":"Exact-match resourceKind filter. Combine with $filter for other predicates."},{"in":"query","name":"resourceUid","schema":{"type":"string"},"description":"Exact-match resourceUid filter. Combine with $filter for other predicates."},{"in":"query","name":"podUid","schema":{"type":"string"},"description":"Exact-match podUid filter. Combine with $filter for other predicates."},{"in":"query","name":"hostingNodeName","schema":{"type":"string"},"description":"Exact-match hostingNodeName filter. Combine with $filter for other predicates."},{"in":"query","name":"cloudInstanceId","schema":{"type":"string"},"description":"Exact-match cloudInstanceId filter. Combine with $filter for other predicates."},{"in":"query","name":"ipAddress","schema":{"type":"string"},"description":"Exact-match ipAddress filter. Combine with $filter for other predicates."},{"in":"query","name":"status","schema":{"type":"string"},"description":"Exact-match status filter. Combine with $filter for other predicates."}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CloudResource"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."},"total":{"type":"integer","description":"Total documents in the collection."},"@odata.count":{"type":"integer","description":"Present only when $count=true: total matches after $filter/$search."},"@odata.truncated":{"type":"boolean","description":"Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."}}}}}},"400":{"description":"Invalid OData query (e.g. malformed $filter)"}}}},"/resources/{id}":{"get":{"summary":"Get cloud resource","description":"Retrieve one inventory resource by its id returned by search, including identity and hosting metadata.","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloudResource"}}}},"404":{"description":"Not found"}}}},"/log-entries":{"get":{"summary":"Search cloud logs","description":"Search timestamped application observations by project, emitting pod or hosting server. Bound the timestamp field to the investigation window. Returns caller/callee identities, request outcome, protocol and operation; observations are evidence, not declared dependencies. Supports OData $filter/$select/$orderby/$search/$top/$skip/$count (MCP uses _filter, _top, etc.). Use $orderby with an id tie-breaker and $top/$skip for stable paging. Nested fields use slash paths. This is a Purple Suite mock API, not a native cloud provider API contract.","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"query","name":"limit","schema":{"type":"integer","default":50},"description":"Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used."},{"in":"query","name":"cursor","schema":{"type":"string"},"description":"Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top."},{"in":"query","name":"$filter","schema":{"type":"string"},"description":"OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."},{"in":"query","name":"$select","schema":{"type":"string"},"description":"Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."},{"in":"query","name":"$orderby","schema":{"type":"string"},"description":"Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."},{"in":"query","name":"$search","schema":{"type":"string"},"description":"Free-text, case-insensitive substring match across the resource's searchable fields."},{"in":"query","name":"$top","schema":{"type":"integer"},"description":"Maximum number of items to return (page size)."},{"in":"query","name":"$skip","schema":{"type":"integer"},"description":"Number of matching items to skip before returning results (offset paging). Combine with $top."},{"in":"query","name":"$count","schema":{"type":"boolean"},"description":"When true, include @odata.count (total matches after $filter/$search, before paging) in the response."},{"in":"query","name":"projectId","schema":{"type":"string"},"description":"Exact-match projectId filter. Combine with $filter for other predicates."},{"in":"query","name":"environment","schema":{"type":"string"},"description":"Exact-match environment filter. Combine with $filter for other predicates."},{"in":"query","name":"serverName","schema":{"type":"string"},"description":"Exact-match serverName filter. Combine with $filter for other predicates."},{"in":"query","name":"podName","schema":{"type":"string"},"description":"Exact-match podName filter. Combine with $filter for other predicates."},{"in":"query","name":"podUid","schema":{"type":"string"},"description":"Exact-match podUid filter. Combine with $filter for other predicates."},{"in":"query","name":"severity","schema":{"type":"string"},"description":"Exact-match severity filter. Combine with $filter for other predicates."}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CloudLogEntry"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."},"total":{"type":"integer","description":"Total documents in the collection."},"@odata.count":{"type":"integer","description":"Present only when $count=true: total matches after $filter/$search."},"@odata.truncated":{"type":"boolean","description":"Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."}}}}}},"400":{"description":"Invalid OData query (e.g. malformed $filter)"}}}},"/log-entries/{id}":{"get":{"summary":"Get cloud log entry","description":"Retrieve one observation by its id for evidence review or citation.","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloudLogEntry"}}}},"404":{"description":"Not found"}}}}}}