> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ndi.nace.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Storage connections

> Authorize customer-owned S3 or Azure Blob storage for new workspaces.

Customer storage is not generally available. Deployments with the feature disabled and no storage configuration return `501 not_implemented`. An enabled but incomplete deployment returns `503 storage_unavailable`. Until the feature is enabled, creating a connection, or a workspace on customer storage, returns `501 not_implemented`. Use `client.storageConnections` to authorize an existing bucket or container before selecting it when creating a workspace.

Prepare cloud trust and permissions with the [customer storage setup guide](https://docs.ndi.nace.ai/guides/customer-storage.md).

```text theme={"dark"}
create(opts: { name: string; destination: StorageDestination; idempotency_key?: string | null }): Promise<StorageConnection>
```

```typescript theme={"dark"}
import { NdiClient } from "ndi-sdk";

const client = new NdiClient();
const connection = await client.storageConnections.create({
  name: "Customer archive",
  destination: { type: "s3", bucket: "customer-data", region: "us-west-2", prefix: "ndi" },
  idempotency_key: "customer-archive-setup",
});
// Configure your IAM role to trust connection.aws_trusted_principal_arn,
// require connection.external_id, and limit access to this bucket/prefix.
await client.storageConnections.setCredentials(connection.id, {
  type: "aws_role", role_arn: "arn:aws:iam::123456789012:role/ndi-workspaces",
});
const validation = await client.storageConnections.validate(connection.id);
await client.jobs.wait(validation.job_id);
const workspace = await client.workspaces.create({
  name: "Customer workspace",
  storage: { type: "external", connection_id: connection.id },
});
```

Azure destinations use `{ type: "azure_blob", account, container, prefix }`. Credentials use `{ type: "azure_service_principal", tenant_id, client_id, client_secret }`. Retrieve the application secret from your secret manager and pass it directly to `setCredentials`; do not log it. Responses contain sanitized metadata, never the secret.

| Operation | Result |
| - | - |
| `create({ name, destination, idempotency_key })` | Draft connection and AWS trust details. |
| `get(connection_id)` | Connection metadata and `status` (`draft`, `ready`, `degraded`, `revoked`). |
| `list({ cursor, limit })` / `iterAll({ limit })` | Connections visible to the caller. |
| `setCredentials(connection_id, credentials)` | Stages credentials for validation. |
| `validate(connection_id, { reactivate })` | Validation job; `reactivate: true` explicitly restores revoked access once any required approval is given. |
| `revoke(connection_id)` | Stops access without deleting objects. |
| `allowReactivation(connection_id)` | Console owner or admin approval for revalidation; access remains revoked. |
| `delete(connection_id)` | Deletes an unused connection; live bindings prevent deletion. |

Creation retries reuse their generated idempotency key. Supply a stable key for retries across process restarts. Rotation preserves the old active identity until validation succeeds. Cancelling validation releases its reservation. A crashed worker can hold a bounded reservation; retry after it expires or explicitly revoke and reactivate; after a console owner or admin revokes, call `allowReactivation` first.

Durable workspace objects use the selected destination. Temporary processing and platform metadata keep their existing placement. External workspaces reject source TTL. Deleting a workspace removes its NDI-owned namespace, while provider versioning and soft-delete policies may retain historical bytes.

A connection with `reactivation_requires_approval=true` cannot be revalidated by any key, console owners and admins included (`403 access_denied`), until a console owner or admin approves it; approval from any other key, including integration, service and console member keys, receives `403 access_denied`. A caller with `can_manage` then validates with explicit reactivation; after member removal or user disablement that is a console owner or admin. Revocation by a console owner or admin key, including of a connection that key created, member removal and user disablement place this hold. Repeating revocation as the owner does not remove it. Tenant administrators may inspect and revoke external-account connections and approve reactivation; only the owning external account may set credentials, validate or delete them.

`revoked_reason` (`owner`, `administrator`, `member_removed`, `user_disabled`) and `revoked_at` record the revocation that removed access or placed the approval hold; later revocations of an already-held connection keep that record. Both remain after reactivation.

Use `can_manage` to decide whether to offer credential updates, validation and unused-connection deletion to the current caller. Read and administrative revocation access can remain available when `can_manage` is false.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.