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

# Transport

> Move governed Storage data between RevoEngine and external HTTP or SFTP systems without loading whole files into low-code memory.

Transport is the integration boundary for file and byte movement. It does not own files, folders, retention, or permissions: `storage.*` does. HTTP and SFTP use the same Storage namespace and ACL path, so a transfer receives exactly the access that its executing identity has in Storage.

<CardGroup cols={2}>
  <Card title="HTTP" icon="globe">
    Use `api.httpCall()` for external API requests, streamed request bodies, and streamed response targets.
  </Card>

  <Card title="SFTP" icon="folder-arrow-down">
    Use `transport.sftpImport()`, `transport.sftpExport()`, and `transport.sftpCommands()` for managed SFTP connections.
  </Card>
</CardGroup>

## One Storage authorization model

Every Storage source and target follows this rule:

```ts theme={null}
{ storageEntryId, namespace?: string }
```

For a new target, place `namespace` beside the normal create-upload fields:

```ts theme={null}
{
  namespace: 'agents',
  parentStorageEntryId: workspaceFolderId,
  name: 'daily.csv',
  contentTypeHint: 'text/csv'
}
```

Omit `namespace` for `explorer`. A provided namespace is not a permission grant: Storage validates its registered namespace policy, the current execution identity, restricted ancestors, and entry ACL. This applies equally to `private/<userId>`, agent workspaces, managed attachments, media, and reports. Read-only or managed namespaces still reject writes when their Storage policy says so.

<Warning>
  Do not replace a Storage check with a namespace check. A transfer never bypasses ACL; a component, Job, Agent, or administrator can act only within the scope Storage authorizes for that execution.
</Warning>

## HTTP streaming

Use `api.httpCall()` to send an existing Storage file without buffering it in the component:

```js theme={null}
const result = await api.httpCall(
  {
    url: 'https://partner.example.com/v1/import',
    method: 'POST',
    headers: { Authorization: `Bearer ${await api.getSecret('PARTNER_API_KEY')}` },
  },
  {
    requestType: 'stream',
    responseType: 'storage',
    source: { storageEntryId: inputFileId, namespace: 'agents' },
    target: {
      namespace: 'agents',
      parentStorageEntryId: outputFolderId,
      name: 'result.csv',
      contentTypeHint: 'text/csv',
    },
  },
);

return result.storage.entry;
```

For a full HTTP contract, status handling, form-data, SSE, and direct upload-session targets, see [HTTP and Storage](/low-code/http-and-storage).

## SFTP

The third argument is an active Secret name, normally a human-readable name such as `SFTP_PRODUCTION`. A UUID is also accepted. The Secret is resolved through its unique name index or primary key; credentials never enter low-code source.

### Connection Secret formats

Password authentication may use flat JSON:

```json theme={null}
{
  "host": "sftp.example.com",
  "port": 22,
  "username": "integration",
  "password": "..."
}
```

or a connection string:

```text theme={null}
sftp://integration:password@sftp.example.com:22
```

Use flat JSON for a private key:

```json theme={null}
{
  "host": "sftp.example.com",
  "port": 22,
  "username": "integration",
  "privateKey": "-----BEGIN OPENSSH PRIVATE KEY-----...",
  "passphrase": "optional"
}
```

The server host key is accepted automatically when no pin is configured. For an installation that requires pinning, add optional `hostKeyFingerprint: "SHA256:..."` to JSON or `?hostKeyFingerprint=SHA256%3A...` to the connection string. Automatic acceptance is stateless: it does not persist a `known_hosts` record.

### Import and export

```js theme={null}
const imported = await transport.sftpImport('/incoming/daily.csv', {
  namespace: 'agents',
  parentStorageEntryId: agentWorkspaceFolderId,
  name: 'daily.csv',
  contentTypeHint: 'text/csv',
  computeStats: 'sync',
}, 'SFTP_PRODUCTION');

await transport.sftpExport(
  '/outgoing/daily.csv',
  { storageEntryId: imported.entry.storageEntryId, namespace: 'agents' },
  'SFTP_PRODUCTION',
);
```

`sftpImport()` streams remote bytes into a direct Storage upload session and returns only after Storage finalizes the entry. `sftpExport()` reads through a short-lived Storage download URL. Neither direction buffers the complete file in low-code memory. `sftpCommands()` is for structured SFTP file operations such as listing, stat, rename, and cleanup; it is not a remote shell.

<Tip>
  The SFTP integration service reuses healthy connections for the same resolved connection configuration. This is internal pooling, not a caller-owned session: code must remain correct if a later call opens a new connection.
</Tip>

## Choose the boundary

| Need                                                                 | Use               |
| -------------------------------------------------------------------- | ----------------- |
| External REST/HTTP API, response streaming, SSE or form-data         | `api.httpCall()`  |
| Remote SFTP file import/export or structured SFTP operations         | `transport.sftp*` |
| Files, folders, ACL, retention, upload sessions, text/document reads | `storage.*`       |

There is no `transport.http` global today. HTTP is already a mature `api.httpCall()` surface; Transport currently adds the Secret-backed SFTP boundary.
