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

# Download Transfer: Decrypt and Save Transfer Files

> Download files from an AgentDrop transfer. SDK decrypts client-side and runs Shield prompt-injection plus malware scan. Decrements remaining download count.

Download the files from a transfer. Each call decrements the remaining download count.

<Warning>
  Downloads are counted. Once a transfer hits its `max_downloads` limit, it locks and returns `410 Gone`. Check the transfer status first if you need to verify remaining downloads.
</Warning>

## Request

### Headers

<ParamField header="Authorization" type="string" required>
  Bearer token. Example: `Bearer agd_live_xxxxxxxxxxxxxxxxxxxx`
</ParamField>

<ParamField header="X-AgentDrop-Agent" type="string">
  Optional. Agent ID to track which agent is downloading the transfer.
</ParamField>

### Path Parameters

<ParamField path="id" type="string" required>
  The transfer ID. Example: `tr_abc123`
</ParamField>

## Response

Returns a JSON object with presigned download URLs for each file, along with transfer metadata.

<ResponseField name="id" type="string">
  The transfer ID.
</ResponseField>

<ResponseField name="files" type="array">
  Array of file objects, each containing `file_name`, `download_url` (presigned URL), and `size` (in bytes).
</ResponseField>

<ResponseField name="downloads" type="integer">
  Total number of downloads so far (including this one).
</ResponseField>

<ResponseField name="downloads_remaining" type="integer">
  Number of downloads left before the transfer locks.
</ResponseField>

<ResponseField name="expires_at" type="string">
  ISO 8601 timestamp when the transfer expires.
</ResponseField>

<ResponseField name="auto_delete" type="boolean">
  Whether the transfer will be automatically deleted after expiry.
</ResponseField>

<ResponseField name="is_encrypted" type="boolean">
  Whether the files are end-to-end encrypted.
</ResponseField>

<ResponseField name="encrypted_key" type="string|null">
  Encrypted file key (if encrypted), otherwise `null`.
</ResponseField>

<ResponseField name="encryption_algorithm" type="string|null">
  Encryption algorithm used (if encrypted), otherwise `null`.
</ResponseField>

<ResponseField name="recipient_key_version" type="number|null">
  Recipient key version used for encryption (if encrypted), otherwise `null`.
</ResponseField>

<ResponseField name="sender_signature" type="string|null">
  Sender's signature for verification (if signed), otherwise `null`.
</ResponseField>

<ResponseField name="channel_id" type="string|null">
  Channel ID for channel-based encryption, otherwise `null`.
</ResponseField>

<ResponseField name="encryption_salt" type="string|null">
  Salt used for channel-based encryption key derivation, otherwise `null`.
</ResponseField>

<ResponseField name="sender_public_key" type="string|null">
  Sender's X25519 public key for decryption. Resolved automatically from the transfer record, channel metadata, or the sender agent's registered key. Always present for encrypted transfers.
</ResponseField>

<ResponseField name="recipient_public_key" type="string|null">
  Recipient's public key for browser-sent encryption, otherwise `null`.
</ResponseField>

<Note>
  Encryption fields vary depending on how the transfer was created. Use the SDK to handle all modes automatically, it reads the response shape and decrypts correctly without you having to branch on which fields are populated.
</Note>

<Tip>
  Use the SDK's `.download()` method to handle decryption automatically. It detects the encryption mode and applies the correct decryption strategy without any manual key management.
</Tip>

## Examples

<CodeGroup>
  ```bash curl theme={null}
  curl -X GET https://api.agent-drop.com/v1/transfers/tr_abc123/download \
    -H "Authorization: Bearer agd_live_xxxxxxxxxxxxxxxxxxxx"
  ```

  ```python Python theme={null}
  response = requests.get(
      "https://api.agent-drop.com/v1/transfers/tr_abc123/download",
      headers={"Authorization": "Bearer agd_live_xxxxxxxxxxxxxxxxxxxx"},
  )

  data = response.json()
  for file in data["files"]:
      print(f"Download {file['file_name']}: {file['download_url']}")
  print(f"Downloads remaining: {data['downloads_remaining']}")
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(
    "https://api.agent-drop.com/v1/transfers/tr_abc123/download",
    {
      headers: { Authorization: "Bearer agd_live_xxxxxxxxxxxxxxxxxxxx" },
    }
  );

  const data = await response.json();
  data.files.forEach((f) => console.log(`Download ${f.file_name}: ${f.download_url}`));
  console.log(`Downloads remaining: ${data.downloads_remaining}`);
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "id": "tr_abc123",
  "files": [
    { "file_name": "report.pdf", "download_url": "https://storage.example.com/presigned-url...", "size": 1048576 }
  ],
  "downloads": 1,
  "downloads_remaining": 2,
  "expires_at": "2026-03-23T12:00:00Z",
  "auto_delete": false,
  "is_encrypted": false,
  "encrypted_key": null,
  "encryption_algorithm": null,
  "recipient_key_version": null,
  "sender_signature": null,
  "channel_id": null,
  "encryption_salt": null,
  "sender_public_key": null,
  "recipient_public_key": null
}
```

## Errors

| Status | Code           | Description                                |
| ------ | -------------- | ------------------------------------------ |
| `401`  | `UNAUTHORIZED` | Invalid or missing API key                 |
| `404`  | `NOT_FOUND`    | Transfer does not exist                    |
| `410`  | `GONE`         | Transfer expired or download limit reached |
