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

# Widget Redirection & Completion

The Spain Public Administration Flow API allows you to control the user's browser redirection behavior once they complete or exit the data extraction widget. This is done by configuring completion settings during session initialization.

***

## Redirection Settings

When calling [Create Session](api:POST/es-public-administration/v1/manager/init), you can customize redirection behaviors under the `settings` object using the following parameters:

* `completion_mode`: The redirection behavior (e.g. standard redirect, opening a new tab, closing the tab, or doing nothing).
* `completion_url`: The destination URL to navigate to (required if redirecting). Supports dynamic template variables in both the URL path and query string.
* `completion_extra_data`: A dictionary of key-value pairs. All keys and values are automatically serialized into query parameters and appended to the final redirect URL. Values in this dictionary support dynamic template variables.

**Authorized Redirect Domains**\
For security reasons (to prevent open-redirect vulnerabilities), the domain of the `completion_url` **must** be pre-authorized and listed in your application's allowed redirect domains. You can configure and manage your authorized domains list in the [Infonite Dashboard](https://infonite.tech/console). Redirection to unlisted domains will fail.

***

## Completion Modes

You can specify how the widget behaves upon exit by setting `completion_mode` to one of the following values:

| Mode             | Behavior                | Description                                                                                          |
| :--------------- | :---------------------- | :--------------------------------------------------------------------------------------------------- |
| `do_nothing`     | Remains on final screen | The widget stops at the final completion screen. The user has to manually close the window or frame. |
| `redirect`       | Redirects current tab   | The browser navigates to the `completion_url` in the same window/tab using `window.location.href`.   |
| `redirect_blank` | Opens a new tab         | The browser opens the `completion_url` in a new window or browser tab.                               |
| `close_tab`      | Closes the tab          | The browser attempts to close the current tab automatically using `window.close()`.                  |

**Prefer `redirect` over `redirect_blank`**\
Modern web browsers restrict scripts from automatically opening new tabs (`window.open(..., '_blank')`) unless triggered by a direct user action (like a click). Since widget redirection is automatic upon flow completion, browsers might block `redirect_blank` as a popup.

**Script Restrictions with `close_tab`**\
Browsers only permit scripts to close tabs that were originally opened by a script (using `window.open`). If the user navigated directly to the widget, `close_tab` will fail silently due to browser security restrictions.

***

## Dynamic Query Parameters & Templating

The widget engine supports dynamic runtime template variables to construct customized redirect flows. These settings are configured inside the `settings` object of the [Create Session](api:POST/es-public-administration/v1/manager/init) initialization request payload.

These template variables can be used in two ways:

1. **Directly in the URL**: Interpolated within the `completion_url` string (either in the URL path or in query parameters).
2. **In Extra Data values**: Interpolated inside the values of the `completion_extra_data` dictionary.

Additionally, the `completion_extra_data` dictionary is **automatically converted to query parameters** and appended to the final URL.

### Available Template Variables

* `{app_id}`: The unique identifier of your application.
* `{customer_id}`: The customer ID associated with the session.
* `{session_id}`: The active widget session ID.
* `{status_code}`: The final status code of the session (e.g. `COMPLETED`).

### Example Parameter Mapping

If you initialize a session with a URL template path and an extra data dictionary (demonstrating how template variables can be partially interpolated inside composite strings, such as prefixing the session ID):

```json
{
  "customer_id": "cust_esp_202406",
  "settings": {
    "completion_mode": "redirect",
    "completion_url": "https://your-server.com/onboarding/{customer_id}/verify",
    "completion_extra_data": {
      "session": "sess_{session_id}",
      "status": "{status_code}",
      "source": "widget_flow"
    }
  }
}
```

The template variables are resolved and the dictionary is serialized into query parameters, redirecting the user to:

```http
https://your-server.com/onboarding/cust_esp_202406/verify?session=sess_644599da47847b79c03cc94f&status=COMPLETED&source=widget_flow
```

**Data Security Warning**\
All redirect parameters, whether interpolated directly in the `completion_url` path or passed via the `completion_extra_data` dictionary, are exposed in the final browser URL query string. This makes them visible in browser history, network logs, and referrer headers. **Never** pass sensitive personal data (such as names, emails, phones, or DNI numbers) as template variables. Use generic identifiers like `{session_id}` and fetch the results securely on your backend.

***

## Real-World Examples

The following examples demonstrate how to construct the request payload when calling the [Create Session](api:POST/es-public-administration/v1/manager/init) endpoint.

Note that all redirection settings must be nested inside the settings object of the initialization payload, alongside other session configurations (such as required features).

#### Return to Host App (CRM / Portal)

Redirect the user back to your customer portal, passing the session status and customer ID to automatically refresh their application state:

```json
{
  "customer_id": "cust_esp_202406",
  "settings": {
    "completion_mode": "redirect",
    "completion_url": "https://portal.mycompany.com/onboarding/verify",
    "completion_extra_data": {
      "session_id": "{session_id}",
      "customer": "{customer_id}",
      "status": "{status_code}"
    }
  }
}
```

**Resulting URL:**\
Assuming a session with ID `644599da47847b79c03cc94f`, customer ID `cust_esp_202406`, and status code `COMPLETED`, the final resolved URL is:

```http
https://portal.mycompany.com/onboarding/verify?session_id=644599da47847b79c03cc94f&customer=cust_esp_202406&status=COMPLETED
```

#### WhatsApp Short URL (wa.me)

The modern way to trigger a WhatsApp chat is using `https://wa.me/<<phone>>`. You can pass a pre-filled template message inside the `text` query parameter:

```json
{
  "customer_id": "cust_esp_202406",
  "settings": {
    "completion_mode": "redirect",
    "completion_url": "https://wa.me/34600000000",
    "completion_extra_data": {
      "text": "I want to apply for my loan. Session ID: {session_id}"
    }
  }
}
```

**Resulting URL:**\
The template variables will be resolved, and the dictionary keys and values will be automatically URL-encoded and appended. For a session with ID `644599da47847b79c03cc94f`, the final resolved URL is:

```http
https://wa.me/34600000000?text=I%20want%20to%20apply%20for%20my%20loan.%20Session%20ID%3A%20644599da47847b79c03cc94f
```

#### WhatsApp Legacy Endpoint

If you need to use the legacy WhatsApp API endpoint (`https://api.whatsapp.com/send`), you pass both the phone number and language settings in `completion_extra_data`:

```json
{
  "customer_id": "cust_esp_202406",
  "settings": {
    "completion_mode": "redirect",
    "completion_url": "https://api.whatsapp.com/send",
    "completion_extra_data": {
      "phone": "34600000000",
      "l": "en",
      "text": "I want to apply for my loan. Session ID: {session_id}"
    }
  }
}
```

**Resulting URL:**\
For a session with ID `644599da47847b79c03cc94f`, the query parameters will be serialized, URL-encoded, and appended:

```http
https://api.whatsapp.com/send?phone=34600000000&l=en&text=I%20want%20to%20apply%20for%20my%20loan.%20Session%20ID%3A%20644599da47847b79c03cc94f
```

***

## Iframe postMessage Events

If you embed the widget inside an HTML `<iframe>` on your website, the widget will dispatch a JavaScript message event to the parent window upon completion or exit, regardless of the configured `completion_mode`. This is useful for programmatically closing the iframe, refreshing parent page components, or showing custom UI elements on the host platform.

### Event Message Structure

When the session reaches its end screen, the widget calls `window.parent.postMessage()` with a payload structured as follows:

* `event`: The event identifier string (always `es-public-administration:ended`).
* `status_code`: The final execution status of the session (e.g. `COMPLETED`, `FAILED`, `WAITING`).
* `cursor`: The identifier of the active cursor/screen the user was on when the widget session finished. Possible values include:
  * `es-public-administration:bye:completed`: The user successfully completed the data retrieval and signature flow.
  * `es-public-administration:bye:failure`: An execution failure occurred and the session ended.
  * `es-public-administration:connect:wait`: The session is temporarily waiting for an asynchronous background task or report generation (e.g., generating the CIRBE report).

**The widget closing is not the session ending.** When `status_code` is `WAITING` and the cursor is `es-public-administration:connect:wait`, the user is free to go but a source — typically the *CIRBE* report — is still working in the background.

This browser message tells you the widget is gone; it does not carry results, and **it is not a completion signal**. The data reaches your backend through webhooks: `es-public-administration:partial` with what was gathered **so far**, and `es-public-administration:ended` with the **final** data once the pending source answers.

A session is finished when — and only when — **`is_closed` is `true`**. See [Knowing when a session is finished](/es-public-administration/overview#knowing-when-a-session-is-finished).

### Integration Example

To handle the completion event in your host application, register a listener for the window `message` event:

```javascript
window.addEventListener("message", (event) => {
  // Recommended: Verify origin to secure the communication channel
  if (event.origin !== "https://infonite.dev") return;

  const payload = event.data;

  // Check if the message is the completion event
  if (payload && payload.event === "es-public-administration:ended") {
    const { status_code, cursor } = payload;
    console.log(`Widget session ended with status: ${status_code} at cursor: ${cursor}`);

    // Programmatically close your iframe modal or update UI
    if (status_code === "COMPLETED") {
      // Refresh user onboarding state or fetch data securely from your backend
    } else {
      // Handle exit/failure scenarios
    }
  }
});
```