> 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.

# Create Session

POST https://widgets.infonite.tech/api/flows/es-public-administration/v1/manager/init
Content-Type: application/json

Initialize a Spain Public Administration flow session to access government portals and retrieve public records.

This endpoint starts the flow lifecycle. Upon successful creation, it returns a `session_id` and a `widget_url`. Your frontend application must redirect the customer to the `widget_url` so they can log in and sign the consent form.

Once the customer completes the widget journey, a webhook notification is delivered (if configured), and the decrypted results can be downloaded using the [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) endpoint.

**What the session reads** is `settings.executions_features`, one code per source — ask only for what you need:

| To read                                                        | Send                           |
| :------------------------------------------------------------- | :----------------------------- |
| The Social Security work history and contribution bases        | `labor_check`                  |
| The public pensions the person receives                        | `public_pensions`              |
| The Banco de España's credit registry (*CIRBE*)                | `credit_registry_data`         |
| The driving licence, and the vehicles registered to the person | `driver_data`, `vehicles_data` |
| The yearly tax situation, from the Agencia Tributaria (*AEAT*) | `yearly_individual_tax`        |
| The academic degrees the person holds                          | `academic_data`                |
| The real estate the person owns                                | `properties_data`              |
| The person's identity, as the administration holds it          | `customer_information_read`    |

So `["credit_registry_data"]` reads the CIRBE report alone, and `["labor_check", "credit_registry_data"]` both. Left out, the session uses your application's default features.

A feature goes as its code alone or as an object with its `configurations`. `yearly_individual_tax` reads the last three closed fiscal years by default, and up to five with `{"code": "yearly_individual_tax", "configurations": {"last_n_years": 5}}`.

Each administration the list reaches is one more identification for the person, one after the other: the Social Security first, then the Agencia Tributaria, then the Carpeta Ciudadana (which serves `academic_data` and `properties_data`). Ask for what you will use.

**`settings.sandbox` is required**: `true` runs against the sandbox, with no real administration behind it; `false` is production.

**Why a session is refused:**

* `400` — the return URL is not one of your application's.
* `403` — a production session from an application that is not in production, or without the capability it needs (`insufficient_capabilities@product:es_public_administration`); a sandbox session needs none.
* `409` — a session with that `customer_id` is already open.

> This endpoint needs to be executed with an app secret, so should always be used in server side without exposing the secret to customers.

Reference: https://infonite.dev/flows/spain-public-administration/api-reference/spain-public-administration-api/sessions-management/flows-es-public-administration-v-1-session-create

## 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

### Body (application/json)

This endpoint expects a FlowStateCreateSchema.

- `customer_id` (string, required) — The external identifier of the customer in your system. Used for auditing and tracking.
- `settings` (FlowSettingsCreateSchema, required) — Custom session settings containing requested features, redirection URLs, and webhook extra parameters.
- `date_limit` (string, optional) — Custom expiration deadline for this session. After this timestamp, the widget cannot be opened.
- `fixed_parameters` (FlowFixedParametersSchema, optional) — Restricted credentials/parameters to enforce during user login in the widget.

## Response

### 200

**200 OK**Flow Session created successfully

- `session_id` (string, required) — The unique database identifier of the created flow session.
- `widget_url` (string, required) — The secure redirection URL where the customer should be sent to open the widget.
- `session_token` (string, required) — Temporary security token scoped for the widget to interact with backend services during the session.

## Errors

### 403 Forbidden Error

**403 Forbidden**The application may not act, or may not open this session

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

### 409 Conflict Error

**409 Conflict**Flow Session already exists

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

### 422 Unprocessable Entity Error

**422 Unprocessable Content**The settings contradict each other

- `detail` (list of ValidationError, required)

## Types

### FlowSettingsCreateSchema

Initialization settings payload for the session.

- `sandbox` (boolean, required) — Set to true to initialize the session in sandbox/test mode.
- `aes_key` (string, optional) — Optional 32-byte base64-encoded AES key. If provided, the final webhook payload will contain the results encrypted with this key. > If the `aes_key` is not specified, the webhook callbacks will not deliver the extracted data payload (it will be omitted/null), and you must retrieve the results manually using the session API. > **Security Best Practice**: Generate a unique, cryptographically secure random 32-byte key dynamically for each session. Never reuse AES keys across different sessions to ensure proper cryptographic isolation.
- `hooks_extra_data` (map from string to string, optional) — Extra data to be included in the hooks. This data will be returned in the webhook as part of the URL as query parameters. This can be useful to include dynamic data in the webhook. There are some template variables that can be used: * `{app_id}`: Your app ID * `{customer_id}`: The customer ID * `{session_id}`: The Session ID * `{parameters_id}`: The unique credentials ID, this identifier is computed by the engine based on the fixed part of the credentials (e.g. the username) * `{producer}`: The producer of the event * `{event}`: The event that triggered the hook * `{status_code}`: The status of the flow at the moment of the event For example, passing: ```json { "custom_param": "customValue", "current_event": "{event}", "status": "{status_code}", "id": "{session_id}" } ``` Will result in the following URL: `https://your.url.com/your/path?custom_param=customValue&current_event=ended&status=COMPLETED&id=67d7d9b72eb0693e7da47a0e` > **Note:** This data is not encrypted and should not contain sensitive information.
- `executions_features` (list of FlowSettingsCreateSchemaExecutionsFeaturesItems, optional) — What the session reads, one feature code per source: `labor_check` (the Social Security work history and contribution bases), `public_pensions`, `credit_registry_data` (the Banco de España's CIRBE report), `driver_data`, `vehicles_data`, `yearly_individual_tax` (the Agencia Tributaria's yearly tax situation), `academic_data` (academic degrees), `properties_data` (real estate held) and `customer_information_read`. Ask only for what you need — `["credit_registry_data"]` reads the CIRBE report and nothing else, and each administration the list reaches is one more identification for the person. Absent: your application's default.
- `allowed_sources` (list of enum, optional) — The administrations the person may be asked to sign in to during this session, **in order of preference**. Absent: your application's default, and without one `["tgss", "aeat", "citizen_folder"]`. One value per administration: `tgss` (Seguridad Social), `aeat` (Agencia Tributaria), `citizen_folder` (Carpeta Ciudadana), `dgt` (Dirección General de Tráfico) and `bde` (Banco de España). A value repeated counts once, at its first position. Each administration the session connects to is a separate sign-in for the person, with its own verification step. The order decides which one goes first: the session asks the first listed administration that returns something still missing, then the next, and never goes back to one it passed over. Put first the administration that returns the most of what you ask for, and the person signs in fewer times. [Choosing the administrations](/flows/spain-public-administration/sources) has what each one returns and what each choice implies. > **It limits connections, not data.** Leaving an administration out means the person is never asked to sign in there, but a feature you request in `executions_features` is still delivered when another allowed connection returns it, whichever administration holds the data: one connection can return features held by more than one administration. What is read is always decided by `executions_features`, never by this list. The exception is `credit_registry_data`: with `bde` in the list, it is read from the Banco de España and from no other connection. > A session whose requested features cannot be served by any of the allowed administrations is refused when it is created, with an error on `settings.allowed_sources` that names the features left without one.
  - Allowed values: `tgss`, `aeat`, `citizen_folder`, `dgt`, `bde`
- `completion_mode` (enum, optional) — The redirection mode to trigger when the user exits the widget.
  - Allowed values: `do_nothing`, `redirect`, `redirect_blank`
- `completion_url` (string, optional) — The target URL where the user is redirected when they complete or exit the widget.
- `completion_extra_data` (map from string to string, optional) — Extra data to be included in the redirect URL. This data will be added to the the url as query parameters. This can be useful to include dynamic data in the webhook. There are some template variables that can be used in the URL: * `{app_id}`: Your app ID * `{customer_id}`: The customer ID * `{session_id}`: The Session ID * `{status_code}`: The final status code of the session For example, passing: ```json { "custom_param": "customValue", "status": "{status_code}", "id": "{session_id}" } ``` Will result in the following URL: `https://your.url.com/your/path?custom_param=customValue&status=COMPLETED&id=67d7d9b72eb0693e7da47a0e` > **Note:** This data is not encrypted and should not contain sensitive information.

### FlowFixedParametersSchema

Predefined and fixed parameters to lock for the public administration engines. All personal data provided in this section must have the proper consent of the user. INFONITE assumes the client has obtained the necessary consent to use this data.

- `user_identification` (string, optional) — Optional Spanish NIF/NIE to enforce during authentication. The widget will restrict access to this user.
- `user_email` (string, optional) — The person's email address. Required when the session asks for `credit_registry_data`: the CIRBE report is requested from the Banco de España with it, whichever administration reads it, and a session that asks for it without one is refused with an error on `fixed_parameters.user_email`. Optional otherwise. When it is given, the person is never asked for it. The email is only used for notification purposes, not for data access.
- `birth_date` (date, optional) — The person's date of birth. Optional. The Banco de España files its credit registry request with it: when it is given, the person is not asked for it there; when it is not, the widget asks them.

### ValidationError

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

### FlowSettingsCreateSchemaExecutionsFeaturesItems

- Feature Code: enum
  - Allowed values: `academic_data`, `credit_registry_data`, `customer_information_read`, `driver_data`, `labor_check`, `properties_data`, `public_pensions`, `vehicles_data`, `yearly_individual_tax`
- FlowSettingsCreateSchemaExecutionsFeaturesItems1

### ValidationErrorLocItems

- string
- integer

### ValidationErrorCtx

### FlowSettingsCreateSchemaExecutionsFeaturesItems1

- `code`: `academic_data` (Academic Data)
  - `configurations` (catgrid_forms__engines__features__public__academic_data__FeatureConfigurations, optional)
- `code`: `credit_registry_data` (Credit Registry Data)
  - `configurations` (catgrid_forms__engines__features__public__credit_registry_data__FeatureConfigurations, optional)
- `code`: `customer_information_read` (Customer Information Read)
  - `configurations` (IFeatureConfigurations, optional) — This feature does not require any configuration
- `code`: `driver_data` (Driver Data)
  - `configurations` (catgrid_forms__engines__features__public__driver_data__FeatureConfigurations, optional)
- `code`: `labor_check` (Labor Check)
  - `configurations` (catgrid_forms__engines__features__public__labor_check__FeatureConfigurations, optional)
- `code`: `properties_data` (Properties Data)
  - `configurations` (catgrid_forms__engines__features__public__properties_data__FeatureConfigurations, optional)
- `code`: `public_pensions` (Public Pensions)
  - `configurations` (IFeatureConfigurations, optional) — This feature does not require any configuration
- `code`: `vehicles_data` (Vehicles Data)
  - `configurations` (catgrid_forms__engines__features__public__vehicles_data__FeatureConfigurations, optional)
- `code`: `yearly_individual_tax` (Yearly Taxes for Individuals)
  - `configurations` (catgrid_forms__engines__features__public__yearly_individual_tax__FeatureConfigurations, optional)

### catgrid_forms__engines__features__public__academic_data__FeatureConfigurations

### catgrid_forms__engines__features__public__credit_registry_data__FeatureConfigurations

- `preferred_type` (enum, optional) — Preferred credit registry report type to retrieve
  - Allowed values: `credit_registry_data:es_cirbe_detailed`, `credit_registry_data:es_cirbe_aggregated`

### IFeatureConfigurations

This feature does not require any configuration

### catgrid_forms__engines__features__public__driver_data__FeatureConfigurations

- `read_point_movements` (boolean, optional, default: false)

### catgrid_forms__engines__features__public__labor_check__FeatureConfigurations

- `from_date` (date, optional, default: 5 years ago) — The date from which the contribution history is returned. Five years back by default; a date further back than eighty years reads eighty, and one less than a year back reads one year.
- `work_life_report` (boolean, optional, default: true) — Whether to attach the official work-life report (PDF) to the labor check. The labor check reads it either way.
- `contribution_base_report` (boolean, optional, default: false) — Whether to attach the official contribution-base report (PDF) to the labor check. The labor check reads it either way.

### catgrid_forms__engines__features__public__properties_data__FeatureConfigurations

### catgrid_forms__engines__features__public__vehicles_data__FeatureConfigurations

- `simple_vehicle_report` (boolean, optional, default: false)

### catgrid_forms__engines__features__public__yearly_individual_tax__FeatureConfigurations

- `last_n_years` (integer, optional, default: 3) — How many closed fiscal years to read, counting back from last year: 1 to 5.

## Examples

**Request**

```json
{
  "customer_id": "cust_esp_202406",
  "settings": {
    "sandbox": true
  }
}
```

**Response**

```json
{
  "session_id": "644599da47847b79c03cc94f",
  "widget_url": "https://widgets.infonite.tech/session/644599da47847b79c03cc94f/start",
  "session_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NDQ1OTlkYTQ3ODQ3Yjc5YzAzY2M5NGYiLCJpYXQiOjE2ODU2MjQwMDB9.4f3b2a1c9d8e7f6a5b4c3d2e1f0a9b8c"
}
```

**SDK Code**

```python
import requests

url = "https://widgets.infonite.tech/api/flows/es-public-administration/v1/manager/init"

payload = {
    "customer_id": "cust_esp_202406",
    "settings": { "sandbox": True }
}
headers = {
    "X-APP-SECRET": "<apiKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://widgets.infonite.tech/api/flows/es-public-administration/v1/manager/init';
const options = {
  method: 'POST',
  headers: {'X-APP-SECRET': '<apiKey>', 'Content-Type': 'application/json'},
  body: '{"customer_id":"cust_esp_202406","settings":{"sandbox":true}}'
};

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"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://widgets.infonite.tech/api/flows/es-public-administration/v1/manager/init"

	payload := strings.NewReader("{\n  \"customer_id\": \"cust_esp_202406\",\n  \"settings\": {\n    \"sandbox\": true\n  }\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("X-APP-SECRET", "<apiKey>")
	req.Header.Add("Content-Type", "application/json")

	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/es-public-administration/v1/manager/init")

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

request = Net::HTTP::Post.new(url)
request["X-APP-SECRET"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"customer_id\": \"cust_esp_202406\",\n  \"settings\": {\n    \"sandbox\": true\n  }\n}"

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.post("https://widgets.infonite.tech/api/flows/es-public-administration/v1/manager/init")
  .header("X-APP-SECRET", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"customer_id\": \"cust_esp_202406\",\n  \"settings\": {\n    \"sandbox\": true\n  }\n}")
  .asString();
```

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

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://widgets.infonite.tech/api/flows/es-public-administration/v1/manager/init', [
  'body' => '{
  "customer_id": "cust_esp_202406",
  "settings": {
    "sandbox": true
  }
}',
  'headers' => [
    'Content-Type' => 'application/json',
    'X-APP-SECRET' => '<apiKey>',
  ],
]);

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

```csharp
using RestSharp;

var client = new RestClient("https://widgets.infonite.tech/api/flows/es-public-administration/v1/manager/init");
var request = new RestRequest(Method.POST);
request.AddHeader("X-APP-SECRET", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"customer_id\": \"cust_esp_202406\",\n  \"settings\": {\n    \"sandbox\": true\n  }\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "X-APP-SECRET": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = [
  "customer_id": "cust_esp_202406",
  "settings": ["sandbox": true]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://widgets.infonite.tech/api/flows/es-public-administration/v1/manager/init")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

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()
```