> This page is for Spain Public Administration Flow.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://infonite.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://infonite.dev/_mcp/server.

# Download Attachments

GET https://widgets.infonite.tech/api/flows/attachments/{session_id}/{attachment_id}

Downloads a document a session delivered with its results — the bytes, exactly as the source issued them.

## Where the id comes from

A document is reported **inside the record it belongs to**, never in a list of its own, and its `id` is the `attachment_id` for this call:

| Flow                        | Where it sits                             | What it is                                                                            |
| :-------------------------- | :---------------------------------------- | :------------------------------------------------------------------------------------ |
| Spain Public Administration | `data.employment.labor_check.attachments` | the official working-life report the labor check was read from                        |
| Financial Rider             | `data.direct_debits[].attachments`        | what the bank keeps for a direct debit, such as the receipt of its most recent charge |

Abbreviated, from Spain Public Administration's results — every value is the one the full example carries, with the rest of the document cut away:

```json
{
  "is_closed": true,
  "retrieved_features": ["customer_information_read", "labor_check"],
  "data": {
    "employment": {
      "labor_check": {
        "product": "social_security",
        "sub_family": "social_security:spain_labor_check",
        "currently_active": true,
        "social_security_days": 6171,
        "attachments": [
          {
            "product": "attachment",
            "sub_family": "attachment:es-tgss-life-report",
            "id": "6aa74d55cacbac2754cb62e4",
            "content_name": "work_life_report.pdf",
            "content_type": "application/pdf",
            "content_hash": "d3df1044751484e3296222487667f33050fda5e1e5d80e159773bbcd2a9540212d5f64afa48e9318b2b93dca1077ad06fd79dfb64c2e58e56c6975528c024a47",
            "metadata": {
              "document-type": "attachment:es-tgss-life-report",
              "document-date": "2026-09-12",
              "csv-code": "DEMO0-3E03C-AB917-846EF-19093-A5A2A"
            }
          }
        ]
      }
    }
  }
}
```

The complete object, in its place in the results, is [`data.employment.labor_check.attachments`](/flows/spain-public-administration/api-reference/spain-public-administration-api/sessions-results/flows-es-public-administration-v-1-session-results#response.body.data.employment.labor_check.attachments) — expand it in the response explorer beside [Get Session Results](https://infonite.dev/flows/spain-public-administration/api-reference/spain-public-administration-api/sessions-results/flows-es-public-administration-v-1-session-results) and you are looking at the same document this endpoint downloads. In Financial Rider the same object sits under each direct debit, in [its results](https://infonite.dev/flows/financial-rider/api-reference/financial-rider-api/sessions-results/flows-financial-rider-v-1-session-results).

`content_hash` is the SHA-512 of the bytes you receive, so you can verify the file you stored is the file we read, and `metadata` carries whatever the source publishes for checking it at origin — for the Social Security report, the CSV code the document itself prints. `content_type` says what the file is: a PDF for an official report, whatever the bank issued for a receipt.

## When to call it

* **Once the session is closed.** An open session answers `412`; one whose last connection is still delivering answers `202` — call again shortly.
* **Rate limit (`429`).** One download every 5 seconds per document.

> Call it from your backend: it takes your application secret, which must never reach your customer's browser.

Reference: https://infonite.dev/flows/spain-public-administration/api-reference/spain-public-administration-api/sessions-results/flows-attachment-download

## Authentication

- `X-APP-SECRET` header (required) — Application Secret

## Servers

- `https://widgets.infonite.tech/api/flows` (Legacy, default)
- `https://widgets.infonite.io/api/flows` (Production)

## Request

### Path parameters

- `session_id` (string, required) — The unique identifier of the flow session where the attachment was retrieved.
- `attachment_id` (string, required) — The unique identifier of the attachment file to download.

## Response

### 200

**200 OK**The file, as the source issued it.

## Errors

### 401 Unauthorized Error

**401 Unauthorized**The application secret was not accepted

- `detail` (string, required) — Error message

### 403 Forbidden Error

**403 Forbidden**The application may not act

- `detail` (string, required) — Error message

### 404 Not Found Error

**404 Not Found**No such session or document for your application

- `detail` (string, required) — Error message

### 422 Unprocessable Entity Error

**422 Unprocessable Content**An id in the path is not one

- `detail` (list of ValidationError, required)

### 429 Too Many Requests Error

**429 Too Many Requests**One download every 5 seconds per document

- `detail` (string, required) — Error message

## Types

### ValidationError

- `loc` (list of ValidationErrorLocItems, required)
- `msg` (string, required)
- `type` (string, required)
- `input` (any, optional)
- `ctx` (ValidationErrorCtx, optional)

### ValidationErrorLocItems

- string
- integer

### ValidationErrorCtx

## Examples

**SDK Code**

```python
import requests

url = "https://widgets.infonite.tech/api/flows/attachments/644599da47847b79c03cc94f/644599da47847b79c03cc950"

headers = {"X-APP-SECRET": "<apiKey>"}

response = requests.get(url, headers=headers)

print(response.json())
```

```javascript
const url = 'https://widgets.infonite.tech/api/flows/attachments/644599da47847b79c03cc94f/644599da47847b79c03cc950';
const options = {method: 'GET', headers: {'X-APP-SECRET': '<apiKey>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://widgets.infonite.tech/api/flows/attachments/644599da47847b79c03cc94f/644599da47847b79c03cc950"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("X-APP-SECRET", "<apiKey>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://widgets.infonite.tech/api/flows/attachments/644599da47847b79c03cc94f/644599da47847b79c03cc950")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["X-APP-SECRET"] = '<apiKey>'

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://widgets.infonite.tech/api/flows/attachments/644599da47847b79c03cc94f/644599da47847b79c03cc950")
  .header("X-APP-SECRET", "<apiKey>")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://widgets.infonite.tech/api/flows/attachments/644599da47847b79c03cc94f/644599da47847b79c03cc950', [
  'headers' => [
    'X-APP-SECRET' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://widgets.infonite.tech/api/flows/attachments/644599da47847b79c03cc94f/644599da47847b79c03cc950");
var request = new RestRequest(Method.GET);
request.AddHeader("X-APP-SECRET", "<apiKey>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["X-APP-SECRET": "<apiKey>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://widgets.infonite.tech/api/flows/attachments/644599da47847b79c03cc94f/644599da47847b79c03cc950")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```