Docs
Open the console →
API reference

API reference: Cloud accounts

Cushy cloud-connections API: list and create accounts (AWS, GCP, Azure, Alibaba), verify health, disconnect/purge/reconnect, sync, read synced inventory, reachability analysis, cross-cloud connections, provisioning opt-in and per-resource metrics endpoints.

Connect and read cloud accounts. Reads are open to any member; mutations require manage_cloud_accounts (SRE · Cloud Admin or Org Admin). Cross-org ids return 404.

GET/api/cloud/accountsSession or Bearer

List your org's cloud accounts with status, health detail and per-type sync runs. Never includes the encrypted connection payload.

Example response
json
{ "accounts": [ { "id": 1, "provider": "AWS", "name": "prod",
  "externalKey": "123456789012", "status": "healthy",
  "healthDetail": { "checks": [ { "name": "sts:AssumeRole", "status": "ok", "checkedAt": "…" } ] },
  "lastValidatedAt": "…", "lastSyncedAt": "…", "runs": [ { "resourceType": "ec2-instance", "stats": {} } ] } ] }
Errors
StatusWhen
401Not signed in.
POST/api/cloud/accountsmanage_cloud_accounts

Create a connection for a provider. Returns 201 with the new account and onboarding references.

Parameters
NameInRequiredDescription
providerbodyyesAWS, GCP, AZR or ALI — all four adapters are real (keyless: role assumption / workload-identity federation only).
namebodynoOptional label.
awsbodyno{ accountId } for AWS (cross-account role + ExternalId).
gcpbodyno{ projectId, projectNumber } for GCP (workload identity federation).
azrbodyno{ tenantId, subscriptionId, clientId? } for Azure (Entra app federated credential — no client secret).
alibodyno{ accountUid, roleArn?, oidcProviderArn? } for Alibaba (RAM OIDC provider + AssumeRoleWithOIDC — no AccessKeySecret).
Example request
json
{ "provider": "AWS", "name": "prod", "aws": { "accountId": "123456789012" } }
Example response
json
{ "id": 1, "…": "onboarding references (ExternalId, role/script details)" }
Errors
StatusWhen
400Unknown provider or invalid input.
403Caller lacks manage_cloud_accounts (audited cloud.rbac_denied).
409Already connected — includes existingId.
503Could not create the account.
POST/api/cloud/accounts/{id}/validatemanage_cloud_accounts

Verify / re-check health. First-ever healthy fires the cutover and kicks the first sync.

Parameters
NameInRequiredDescription
idpathyesAccount id in your org.
Example response
json
{ "status": "healthy", "identity": { "…": "…" }, "checks": [ { "name": "…", "status": "ok" } ] }
Errors
StatusWhen
403Caller lacks manage_cloud_accounts.
404No such account in your org.
409Account disconnected, or a check failed — carries missing for a permission error.
503Transient provider error — retry.
POST/api/cloud/accounts/{id}/syncmanage_cloud_accounts

Bounded on-demand sync of one healthy account. Returns 202 with the pass summary.

Parameters
NameInRequiredDescription
idpathyesAccount id in your org.
Example response
json
{ "queued": 4, "completed": 3, "deferred": 1 }
Errors
StatusWhen
403Caller lacks manage_cloud_accounts.
404No such account in your org.
409Connection is not healthy — fix health first.
DELETE/api/cloud/accounts/{id}manage_cloud_accounts

Disconnect (never purge). Syncing stops; discovered rows are marked account_disconnected.

Parameters
NameInRequiredDescription
idpathyesAccount id in your org.
Example response
json
{ "ok": true, "reminder": "delete the IAM role / federation config to fully de-provision." }
Errors
StatusWhen
403Caller lacks manage_cloud_accounts.
404No such account in your org.
409Already disconnected.
GET/api/cloud/resourcesSession or Bearer

Keyset-paginated synced inventory. Filter and page through your resources.

Parameters
NameInRequiredDescription
accountIdquerynoOwning cloud-account id (positive integer) — return only that connected account's resources.
providerquerynoAWS / GCP / AZR / ALI.
typequerynoResource type filter.
regionquerynoRegion filter.
qquerynoFree-text search over name and native id.
limitqueryno1–200 (default 100).
cursorquerynoKeyset cursor from a prior nextCursor.
includeDeletedqueryno1 to include soft-deleted rows with reason.
Example response
json
{ "resources": [ { "id": 42, "accountId": 7, "provider": "AWS", "resourceType": "network",
  "nativeId": "vpc-0abc…", "name": "prod-use1", "region": "us-east-1", "status": "ok",
  "attributes": {}, "providerDetail": {}, "tags": {}, "firstSeenAt": "…", "lastSeenAt": "…",
  "deletedAt": null, "deletedReason": null,
  "managed": false, "managedByWorkspaceId": null } ], "nextCursor": 42 }
Errors
StatusWhen
400limit out of 1–200, a negative cursor, or a non-positive-integer accountId.
401Not signed in / invalid token.
POST/api/cloud/reachabilitySession

Network reachability / path analysis (NET), provider-aware across all four clouds. Given a source and destination IP, returns whether traffic is reachable and the ordered per-layer hop list (on AWS: security-group egress → NACL egress → route → gateway → NACL ingress → security-group ingress + the stateless return path; Azure NSGs / GCP VPC firewalls / Alibaba security groups use their own layers and defaults), naming the blocking layer.

Parameters
NameInRequiredDescription
sourceIpbodyyesSource IPv4 address.
destIpbodyyesDestination IPv4 address.
protocolbodynotcp | udp | icmp | all (default tcp).
portbodyno0–65535 (default 443).
sourceComponentIdbodynoA resource nativeId to disambiguate the source.
destComponentIdbodynoA resource nativeId to disambiguate the destination.
Example response
json
{ "reachable": false, "protocol": "tcp", "port": 443,
  "source": { "kind": "instance", "id": "i-0a…", "name": "prod-node-01", "ip": "10.0.1.41" },
  "destination": { "kind": "instance", "id": "i-0a…", "name": "prod-node-02", "ip": "10.0.2.41" },
  "hops": [ { "layer": "Destination security group (ingress)", "direction": "ingress",
    "component": { "id": "sg-0a…app1", "name": "app", "kind": "securitygroup" },
    "verdict": "deny", "explanation": "No ingress rule allows tcp/443 from 10.0.1.41." } ],
  "blockedAt": 5, "summary": "Not reachable: blocked at destination security group (ingress) …" }
Errors
StatusWhen
400sourceIp/destIp is not a valid IPv4, bad protocol, or port out of range.
401Not signed in.
200A never-connected org (no topology) returns { empty: true }.
POST/api/cloud/accounts/{id}/provisioningmanage_cloud_accounts

Enable or disable REAL Terraform provisioning for this account (default OFF). Enabling requires an explicit acknowledgement — it authorizes real, billable resource creation with short-lived write credentials. Audited cloud.provisioning_enabled/disabled.

Parameters
NameInRequiredDescription
idpathyesAccount id in your org.
enabledbodyyestrue or false.
ackbodynoRequired (true) when enabling.
Example request
json
{ "enabled": true, "ack": true }
Example response
json
{ "ok": true, "account": { "id": 1, "provisioningEnabled": true, "…": "…" } }
Errors
StatusWhen
400Enabling without the acknowledgement.
403Caller lacks manage_cloud_accounts (Viewer/Deployer).
404No such account in your org.
GET/api/cloud/connectionsSession

List your org's cross-cloud / peering connections (site-to-site VPN across any of the six cloud pairs, or same-provider peering drawn on the topology map). The pre-shared key is never returned — the DTO carries secretSet only.

Example response
json
{ "connections": [ { "id": 3, "connType": "s2s_vpn", "status": "up",
  "a": { "provider": "AWS", "networkNativeId": "vpc-0a…", "region": "us-east-1" },
  "b": { "provider": "AZR", "networkNativeId": "/subscriptions/…/vnet-prod", "region": "eastus" },
  "workspaceId": 9, "secretSet": true, "tunnels": [ { "state": "up" }, { "state": "up" } ] } ] }
Errors
StatusWhen
401Not signed in.
POST/api/cloud/connectionsmanage_cloud_accounts

Create a connection between two healthy accounts (different providers ⇒ s2s_vpn; same provider ⇒ peering) and kick auto-provisioning through the Terraform engine. Also accepts the topology editor's draw payload (aNativeId/bNativeId endpoints + direction + rules + emitEgress). DELETE /api/cloud/connections/{id} soft-deletes.

Parameters
NameInRequiredDescription
aAccountId / bAccountIdbodynoThe two account ids (classic path).
aNativeId / bNativeIdbodynoTwo network/subnet native ids (draw-a-connector path).
connTypebodynos2s_vpn | peering | interconnect (interconnect is tracked model-only, never auto-provisioned).
direction / rules / emitEgressbodynoConnector direction, allow rules and egress emission (topology editor).
Example response
json
{ "connection": { "id": 3, "status": "planned", "…": "…" } }
Errors
StatusWhen
400Unhealthy/unknown account, bad CIDR/port, or an invalid connType.
403Caller lacks manage_cloud_accounts (Viewer).
404Cross-org account/endpoint ids.
GET/api/cloud/resources/{nativeId}/metrics-endpointmanage_cloud_accounts (writes); managers view

Per-resource metrics endpoint: GET returns the resolved non-secret config + per-provider collector install instructions; PUT saves { baseUrl, token?, labelFilter? } (https-only; the bearer token is encrypted and never echoed); DELETE removes it. The endpoint is keyed to the synced resource's native id — the collector agent lives on that resource.

Errors
StatusWhen
400Non-https base URL or a malformed config.
403Write without manage_cloud_accounts.
404Unknown/cross-org native id.
GET/api/cloud/settingsmanage_cloud_accounts (view)

Cushy's trust anchors for onboarding (the platform AWS account id and OIDC/WIF issuer your cloud-side role or federation must trust). Managers view them so the connect instructions render real values; they are managed by Cushy.

Disconnect vs delete. DELETE /api/cloud/accounts/{id} is a SOFT disconnect (rows retained, marked account_disconnected); DELETE /api/cloud/accounts/{id}?purge=1 is the explicit hard delete of an already-disconnected account — it removes the account row and all its synced data, and answers 409 for a live account or one that still has Terraform workspaces or a live connection. A disconnected account can also be reconnected: a healthy POST …/validate flips it back to active and kicks a fresh sync.