---
title: Filter Hooks
slug: filter-hooks
type: reference
plugin: huxx-connect
summary: Every filter HuxxConnect and HuxxConnect Pro expose, grouped by area, for reshaping requests, responses, authentication, caching, display, and sync behavior. Pro hooks are marked.
keywords: [hooks, filters, reference, extensibility]
version_added: 1.2.5
version_updated: 1.2.5
level: advanced
---

New to hooks? Most customizations never need them — start with the [how-to guides](/docs/connect/how-to/use-template-functions/). This page is the complete filter reference, grouped by area; every entry links to its source and shows its signature.

## Requests

### huxx_connect/request/args

Filter the final request args for wp_remote_request().

**Type:** filter · **Since:** — · **Source:** `src/Http/RequestBuilder.php`

```php
apply_filters( 'huxx_connect/request/args', $request_args, $api, $endpoint, $args, $resolved_params );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$request_args` | `array` | The request arguments (method, timeout, headers, body, etc.). |
| `$api` | `Api` | The API configuration. |
| `$endpoint` | `Endpoint` | The endpoint configuration. |
| `$args` | `array` | Runtime arguments. |
| `$resolved_params` | `array` | Resolved request params after |

<!-- example:huxx_connect/request/args -->
```php
add_filter( 'huxx_connect/request/args', function ( array $request_args, $api ) {
    if ( 'demo_api' === $api->get_id() ) {
        $request_args['headers']['X-Trace-Id'] = wp_generate_uuid4();
    }
    return $request_args;
}, 10, 2 );
```

Every request to the `demo_api` API now carries an `X-Trace-Id` header. Runs after body encoding — `$request_args['body']` is already the encoded string, not the parameter array.
<!-- /example:huxx_connect/request/args -->

### huxx_connect/request/body_params

Filter body parameters.

**Type:** filter · **Since:** — · **Source:** `src/Http/RequestBuilder.php`

```php
apply_filters( 'huxx_connect/request/body_params', $body_params, $api, $endpoint );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$body_params` | `array` | The body parameters. |
| `$api` | `Api` | The API configuration. |
| `$endpoint` | `Endpoint` | The endpoint configuration. |

<!-- example:huxx_connect/request/body_params -->
Add a field to the outgoing request body for one endpoint. Runs after body params are built from the endpoint config, so returning the array is required.

```php
// Stamp every write to demo_api's create_product endpoint with a source marker.
add_filter( 'huxx_connect/request/body_params', function ( $body_params, $api, $endpoint ) {
    if ( $api->get_id() === 'demo_api' && $endpoint->get_id() === 'create_product' ) {
        $body_params['source'] = 'wordpress';
    }
    return $body_params;
}, 10, 3 );
```

Result: the JSON body sent to `https://dummyjson.com/products/add` now includes `"source":"wordpress"`.
<!-- /example:huxx_connect/request/body_params -->

### huxx_connect/request/default_args

Filter the default request arguments.

**Type:** filter · **Since:** — · **Source:** `src/Http/Client.php`

```php
apply_filters( 'huxx_connect/request/default_args', $defaults );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$defaults` | `array` | Default request arguments. |

<!-- example:huxx_connect/request/default_args -->
Change the baseline `wp_safe_remote_request()` arguments applied to every HuxxConnect call. This filter passes a single argument, so no accepted-args count is needed.

```php
// Raise the default HTTP timeout for all HuxxConnect requests from 10s to 30s.
add_filter( 'huxx_connect/request/default_args', function ( $defaults ) {
    $defaults['timeout'] = 30;
    return $defaults;
} );
```

Result: requests that don't set their own timeout now wait up to 30 seconds instead of 10.
<!-- /example:huxx_connect/request/default_args -->

### huxx_connect/request/endpoint_path

Filter the endpoint path.

**Type:** filter · **Since:** — · **Source:** `src/Http/RequestBuilder.php`

```php
apply_filters( 'huxx_connect/request/endpoint_path', $path, $api, $endpoint, $args );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$path` | `string` | The resolved endpoint path. |
| `$api` | `Api` | The API configuration. |
| `$endpoint` | `Endpoint` | The endpoint configuration. |
| `$args` | `array` | Runtime arguments. |

<!-- example:huxx_connect/request/endpoint_path -->
Rewrite the resolved endpoint path before it is joined to the API base URL.

```php
// Route demo_api's get_products endpoint to a single category.
add_filter( 'huxx_connect/request/endpoint_path', function ( $path, $api, $endpoint, $args ) {
    if ( $api->get_id() === 'demo_api' && $endpoint->get_id() === 'get_products' ) {
        return 'products/category/smartphones';
    }
    return $path;
}, 10, 4 );
```

Result: the request goes to `https://dummyjson.com/products/category/smartphones` instead of `/products`.
<!-- /example:huxx_connect/request/endpoint_path -->

### huxx_connect/request/header_params

Filter header parameters (includes any auth-injected headers).

**Type:** filter · **Since:** — · **Source:** `src/Http/RequestBuilder.php`

```php
apply_filters( 'huxx_connect/request/header_params', $headers, $api, $endpoint );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$headers` | `array` | The request headers. |
| `$api` | `Api` | The API configuration. |
| `$endpoint` | `Endpoint` | The endpoint configuration. |

<!-- example:huxx_connect/request/header_params -->
Add or adjust request headers. Any auth-injected headers are already present in `$headers` at this point, so returning the merged array is required.

```php
// Add a tracking header to every demo_api request.
add_filter( 'huxx_connect/request/header_params', function ( $headers, $api, $endpoint ) {
    if ( $api->get_id() === 'demo_api' ) {
        $headers['X-Request-Source'] = 'huxx-connect';
    }
    return $headers;
}, 10, 3 );
```

Result: outgoing demo_api requests carry `X-Request-Source: huxx-connect` alongside the existing headers.
<!-- /example:huxx_connect/request/header_params -->

### huxx_connect/request/query_params

Filter query parameters (includes any auth-injected query params).

**Type:** filter · **Since:** — · **Source:** `src/Http/RequestBuilder.php`

```php
apply_filters( 'huxx_connect/request/query_params', $query_params, $api, $endpoint );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$query_params` | `array` | The query parameters. |
| `$api` | `Api` | The API configuration. |
| `$endpoint` | `Endpoint` | The endpoint configuration. |

<!-- example:huxx_connect/request/query_params -->
Add or override query-string parameters. Auth-injected query params are already merged in, so return the full array.

```php
// Cap demo_api's get_products result set at 10 items.
add_filter( 'huxx_connect/request/query_params', function ( $query_params, $api, $endpoint ) {
    if ( $api->get_id() === 'demo_api' && $endpoint->get_id() === 'get_products' ) {
        $query_params['limit'] = 10;
    }
    return $query_params;
}, 10, 3 );
```

Result: the request URL becomes `https://dummyjson.com/products?limit=10`.
<!-- /example:huxx_connect/request/query_params -->

### huxx_connect/request/should_execute

Filter whether the request should execute.

**Type:** filter · **Since:** — · **Source:** `src/Core/Executor.php`

```php
apply_filters( 'huxx_connect/request/should_execute' );
```

<!-- example:huxx_connect/request/should_execute -->
Return `false` to abort a request before any HTTP call is made — useful as a kill switch. Returning the incoming boolean leaves the request untouched.

```php
// Skip all demo_api traffic while this option flag is set.
add_filter( 'huxx_connect/request/should_execute', function ( $should_execute, $api, $endpoint, $args ) {
    if ( $api->get_id() === 'demo_api' && get_option( 'demo_api_paused' ) ) {
        return false;
    }
    return $should_execute;
}, 10, 4 );
```

Result: while paused, the Executor short-circuits and returns an ErrorResponse ("Request execution was prevented by a filter.") without touching the network.
<!-- /example:huxx_connect/request/should_execute -->

### huxx_connect/request/url

Filter the final URL (after query parameters are appended).

**Type:** filter · **Since:** — · **Source:** `src/Http/RequestBuilder.php`

```php
apply_filters( 'huxx_connect/request/url', $url, $api, $endpoint, $args, $resolved_params );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$url` | `string` | The full request URL. |
| `$api` | `Api` | The API configuration. |
| `$endpoint` | `Endpoint` | The endpoint configuration. |
| `$args` | `array` | Runtime arguments. |
| `$resolved_params` | `array` | Resolved request params after |

<!-- example:huxx_connect/request/url -->
Rewrite the final URL after query parameters have been appended — for example to point traffic at a mirror without editing the saved base URL.

```php
// Send demo_api traffic to a staging mirror.
add_filter( 'huxx_connect/request/url', function ( $url, $api, $endpoint, $args, $resolved_params ) {
    if ( $api->get_id() === 'demo_api' ) {
        return str_replace( 'https://dummyjson.com', 'https://staging.dummyjson.com', $url );
    }
    return $url;
}, 10, 5 );
```

Result: `https://dummyjson.com/products?limit=10` is sent to `https://staging.dummyjson.com/products?limit=10`.
<!-- /example:huxx_connect/request/url -->

## Responses

### huxx_connect/executor/result

Filter the final execution result before it is returned.

**Type:** filter · **Since:** — · **Source:** `src/Core/Executor.php`

```php
apply_filters( 'huxx_connect/executor/result', $data, $result, $api, $endpoint, $args );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$data` | `mixed` | The response body data. |
| `$result` | `Response` | The full Response object. |
| `$api` | `Api` | The API being called. |
| `$endpoint` | `Endpoint` | The endpoint being called. |
| `$args` | `array` | Runtime arguments. |

<!-- example:huxx_connect/executor/result -->
Reshape the final result callers receive from `demo_api`/`get_products` — return just the products list plus a fetch timestamp. This filter fires only on success, so `$result` is always a `Response`.

```php
add_filter( 'huxx_connect/executor/result', function ( $data, $result, $api, $endpoint, $args ) {
    if ( $api->get_id() !== 'demo_api' || $endpoint->get_id() !== 'get_products' ) {
        return $data;
    }
    if ( ! is_array( $data ) || ! isset( $data['products'] ) ) {
        return $data;
    }

    return array(
        'items'      => $data['products'],
        'fetched_at' => current_time( 'mysql' ),
    );
}, 10, 5 );
```

Result: callers of `demo_api`/`get_products` now receive `[ 'items' => [...], 'fetched_at' => '2026-07-20 09:14:03' ]` instead of the raw `{ products, total, skip, limit }` envelope.
<!-- /example:huxx_connect/executor/result -->

### huxx_connect/response/body

Filter the parsed response body.

**Type:** filter · **Since:** — · **Source:** `src/Http/ResponseParser.php`

```php
apply_filters( 'huxx_connect/response/body' );
```

<!-- example:huxx_connect/response/body -->
Transform the parsed body before it becomes the `Response` (and before it's cached). This adds a computed `sale_price` to every `get_products` item.

```php
add_filter( 'huxx_connect/response/body', function ( $body, $raw_response, $api, $endpoint ) {
    if ( $endpoint->get_id() !== 'get_products' || ! is_array( $body ) || empty( $body['products'] ) ) {
        return $body;
    }

    foreach ( $body['products'] as &$product ) {
        $product['sale_price'] = round( $product['price'] * 0.9, 2 );
    }
    unset( $product );

    return $body;
}, 10, 4 );
```

Result: each product in the parsed body gains a `sale_price` key (10% off its `price`) before the value is cached and returned.
<!-- /example:huxx_connect/response/body -->

### huxx_connect/response/data

Filter the final response data.

**Type:** filter · **Since:** — · **Source:** `src/Http/ResponseParser.php`

```php
apply_filters( 'huxx_connect/response/data' );
```

<!-- example:huxx_connect/response/data -->
The last filter in the parse pipeline; it receives the fully built `Response` object. This example logs an audit line for every `demo_api` response and returns the object untouched.

```php
add_filter( 'huxx_connect/response/data', function ( $response, $api, $endpoint ) {
    if ( $api->get_id() === 'demo_api' ) {
        error_log( sprintf(
            '[HuxxConnect] %s/%s -> HTTP %d',
            $api->get_id(),
            $endpoint->get_id(),
            $response->get_status_code()
        ) );
    }

    return $response;
}, 10, 3 );
```

Result: the debug log records a line like `[HuxxConnect] demo_api/get_products -> HTTP 200` for each successful call.
<!-- /example:huxx_connect/response/data -->

### huxx_connect/response/error

Filter error responses.

**Type:** filter · **Since:** — · **Source:** `src/Http/ResponseParser.php`

```php
apply_filters( 'huxx_connect/response/error' );
```

<!-- example:huxx_connect/response/error -->
Fires only when the response is an HTTP error (4xx/5xx). Return an `ErrorResponse` — here a rate-limit (429) is swapped for a clearer operator-facing message while preserving the original context.

```php
add_filter( 'huxx_connect/response/error', function ( $error, $raw_response, $api, $endpoint ) {
    if ( $api->get_id() === 'demo_api' && $error->get_code() === 429 ) {
        return new HuxxConnectCoreErrorResponse(
            429,
            'demo_api is rate-limited — retrying shortly.',
            $raw_response,
            $error->get_context()
        );
    }

    return $error;
}, 10, 4 );
```

Result: a 429 from `demo_api` surfaces the message `demo_api is rate-limited — retrying shortly.` instead of the raw upstream error text.
<!-- /example:huxx_connect/response/error -->

### huxx_connect/response/headers

Filter response headers.

**Type:** filter · **Since:** — · **Source:** `src/Http/ResponseParser.php`

```php
apply_filters( 'huxx_connect/response/headers' );
```

<!-- example:huxx_connect/response/headers -->
Read upstream response headers as they pass through. This example stashes the remaining rate-limit budget in a transient; the callback returns `$headers` unchanged (it observes, it doesn't rewrite).

```php
add_filter( 'huxx_connect/response/headers', function ( $headers, $raw_response, $api, $endpoint ) {
    if ( isset( $headers['x-ratelimit-remaining'] ) ) {
        set_transient(
            'huxx_ratelimit_' . $api->get_id(),
            (int) $headers['x-ratelimit-remaining'],
            60
        );
    }

    return $headers;
}, 10, 4 );
```

Result: after each call, `get_transient( 'huxx_ratelimit_demo_api' )` holds the API's reported remaining request count.
<!-- /example:huxx_connect/response/headers -->

### huxx_connect/response/raw

Filter the raw response before processing.

**Type:** filter · **Since:** — · **Source:** `src/Http/ResponseParser.php`

```php
apply_filters( 'huxx_connect/response/raw' );
```

<!-- example:huxx_connect/response/raw -->
Runs before the body is parsed, so it's the place to repair a raw payload the JSON decoder can't read. Here it strips the anti-JSON-hijacking prefix (`)]}',n`) some APIs prepend. Downstream values are re-extracted after this filter, so the parser sees the cleaned body.

```php
add_filter( 'huxx_connect/response/raw', function ( $raw_response, $status_code, $api, $endpoint ) {
    if ( $api->get_id() !== 'demo_api' || is_wp_error( $raw_response ) ) {
        return $raw_response;
    }

    $body = wp_remote_retrieve_body( $raw_response );
    if ( strpos( $body, ")]}',n" ) === 0 ) {
        $raw_response['body'] = substr( $body, 6 );
    }

    return $raw_response;
}, 10, 4 );
```

Result: a body of `)]}',n{"products":[...]}` reaches the parser as clean `{"products":[...]}` and decodes normally.
<!-- /example:huxx_connect/response/raw -->

## Authentication

### huxx_connect/auth/config

Filter the auth config before creating the provider.

**Type:** filter · **Since:** — · **Source:** `src/Auth/AuthManager.php`

```php
apply_filters( 'huxx_connect/auth/config', $auth_config, $auth_type, $api, $endpoint );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$auth_config` | `array` | The auth configuration. |
| `$auth_type` | `string` | The auth type. |
| `$api` | `Api` | The API object. |
| `$endpoint` | `Endpoint` | The endpoint object. |

<!-- example:huxx_connect/auth/config -->
Rewrite stored credentials at resolve time — here, pull the `demo_api` bearer token from an environment variable instead of the saved config so the secret never lives in the database.

```php
add_filter( 'huxx_connect/auth/config', function ( $auth_config, $auth_type, $api, $endpoint ) {
	if ( 'bearer' === $auth_type && 'demo_api' === $api->get_id() ) {
		$env = getenv( 'DEMO_API_TOKEN' );
		if ( false !== $env && '' !== $env ) {
			$auth_config['token'] = $env;
		}
	}

	return $auth_config;
}, 10, 4 );
```

Result: requests to `demo_api` send `Authorization: Bearer <value of $DEMO_API_TOKEN>`, falling back to the saved token when the variable is unset.
<!-- /example:huxx_connect/auth/config -->

### huxx_connect/auth/custom

Apply custom authentication.

**Type:** filter · **Since:** — · **Source:** `src/Auth/Providers/CustomAuth.php`

```php
apply_filters( 'huxx_connect/auth/custom', $result, $config );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$result` | `array` | Current request parts: url, headers, query. |
| `$config` | `array` | The custom auth configuration. |

<!-- example:huxx_connect/auth/custom -->
The `custom` auth type delegates entirely to this filter. The callback receives the current request parts (`url`, `headers`, `query`) and returns the modified set — here it signs each request with an HMAC of the URL plus a timestamp.

```php
add_filter( 'huxx_connect/auth/custom', function ( $result, $config ) {
	$secret    = $config['shared_secret'] ?? 'hxc_example_token_123';
	$timestamp = (string) time();

	$result['headers']['X-Timestamp'] = $timestamp;
	$result['headers']['X-Signature'] = hash_hmac( 'sha256', $result['url'] . $timestamp, $secret );

	return $result;
}, 10, 2 );
```

Result: every request using the `custom` auth type gains `X-Timestamp` and `X-Signature` headers, with the signature computed from the outgoing URL.
<!-- /example:huxx_connect/auth/custom -->

### huxx_connect/auth/provider

Filter the auth provider.

**Type:** filter · **Since:** — · **Source:** `src/Auth/AuthManager.php`

```php
apply_filters( 'huxx_connect/auth/provider', $provider, $api, $endpoint, $auth_config );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$provider` | `AuthProviderInterface` | The auth provider. |
| `$api` | `Api` | The API object. |
| `$endpoint` | `Endpoint` | The endpoint object. |
| `$auth_config` | `array` | The auth configuration. |

<!-- example:huxx_connect/auth/provider -->
Runs after the provider is constructed, so you can inspect (or replace/decorate) it. This example just logs which provider resolved for each API/endpoint pair.

```php
add_filter( 'huxx_connect/auth/provider', function ( $provider, $api, $endpoint, $auth_config ) {
	error_log( sprintf(
		'HuxxConnect auth: %s/%s resolved to "%s"',
		$api->get_id(),
		$endpoint->get_id(),
		$provider->get_type()
	) );

	return $provider;
}, 10, 4 );
```

Result (error log): `HuxxConnect auth: demo_api/get_products resolved to "bearer"`. Return a different object implementing `AuthProviderInterface` to swap the provider entirely.
<!-- /example:huxx_connect/auth/provider -->

### huxx_connect/auth/token

Filter the API key before it is applied to the request.

**Type:** filter · **Since:** — · **Source:** `src/Auth/Providers/ApiKeyAuth.php`

```php
apply_filters( 'huxx_connect/auth/token', $key, $api, $endpoint );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$key` | `string` | The API key value. |
| `$api` | `Api|null` | The API context, or null when constructed without it. |
| `$endpoint` | `Endpoint|null` | The endpoint context, or null when constructed without it. |

<!-- example:huxx_connect/auth/token -->
Filters the bearer token just before it becomes the `Authorization: Bearer …` header. `$api` and `$endpoint` may be `null` when the provider was built without context, so guard before calling methods on them.

```php
add_filter( 'huxx_connect/auth/token', function ( $token, $api, $endpoint ) {
	if ( $api && 'demo_api' === $api->get_id() ) {
		return 'hxc_example_token_123';
	}

	return $token;
}, 10, 3 );
```

Result: bearer requests to `demo_api` send `Authorization: Bearer hxc_example_token_123`; every other API keeps its own stored token.
<!-- /example:huxx_connect/auth/token -->

### huxx_connect/auth/types

Filter the list of available authentication types.

**Type:** filter · **Since:** — · **Source:** `src/Admin/AdminPage.php`

```php
apply_filters( 'huxx_connect/auth/types', $types );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$types` | `array` | Map of auth-type slug => display label. |

<!-- example:huxx_connect/auth/types -->
Register an additional auth type in the admin dropdowns (the "Add New API" modal and the API detail page). The key is the stored slug; the value is the display label. Pair a new slug with the `huxx_connect/auth/provider` filter to supply its provider.

```php
add_filter( 'huxx_connect/auth/types', function ( $types ) {
	$types['hmac_signed'] = __( 'HMAC Signed', 'my-plugin' );

	return $types;
}, 10, 1 );
```

Result: the auth-type selector lists "HMAC Signed" alongside the built-in None, Bearer Token, API Key, Basic Auth, and Custom (Hook) options.
<!-- /example:huxx_connect/auth/types -->

## Caching

### huxx_connect/cache/default_stale_ttl

Filter the global default stale cache TTL.

**Type:** filter · **Since:** — · **Source:** `src/Core/Executor.php`

```php
apply_filters( 'huxx_connect/cache/default_stale_ttl', $default_stale_ttl );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$default_stale_ttl` | `int` | Default stale TTL in seconds (0 = disabled). |

<!-- example:huxx_connect/cache/default_stale_ttl -->
Set the fallback stale-cache lifetime for endpoints that don't define their own `stale_cache_ttl`. This filter only runs when the endpoint's own stale TTL is `0`.

```php
add_filter( 'huxx_connect/cache/default_stale_ttl', function ( $default_stale_ttl ) {
	// Keep a 24-hour stale copy for any endpoint that doesn't set its own.
	return 24 * HOUR_IN_SECONDS;
} );
```

Result: endpoints without an explicit stale TTL now retain a 24-hour stale copy, served if the live API errors after the fresh cache expires.
<!-- /example:huxx_connect/cache/default_stale_ttl -->

### huxx_connect/cache/should_cache

Filter whether this response should be cached.

**Type:** filter · **Since:** — · **Source:** `src/Cache/CacheManager.php`

```php
apply_filters( 'huxx_connect/cache/should_cache', $should_cache, $api_id, $endpoint_id, $data );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$should_cache` | `bool` | Whether to cache. |
| `$api_id` | `string` | API identifier. |
| `$endpoint_id` | `string` | Endpoint identifier. |
| `$data` | `mixed` | Response data. |

<!-- example:huxx_connect/cache/should_cache -->
Decide whether a given response is worth storing. Return `false` to skip caching this one response; return the first argument unchanged to keep the default behavior.

```php
add_filter( 'huxx_connect/cache/should_cache', function ( $should_cache, $api_id, $endpoint_id, $data ) {
	// Never cache an empty product list from demo_api/get_products.
	if ( $api_id === 'demo_api' && $endpoint_id === 'get_products' ) {
		if ( empty( $data['products'] ) ) {
			return false;
		}
	}
	return $should_cache;
}, 10, 4 );
```

Result: an empty `products` payload is not cached, so the next request retries the API instead of serving an empty list.
<!-- /example:huxx_connect/cache/should_cache -->

### huxx_connect/cache/stale_ttl

Filter the stale cache TTL.

**Type:** filter · **Since:** — · **Source:** `src/Cache/CacheManager.php`

```php
apply_filters( 'huxx_connect/cache/stale_ttl', $stale_ttl, $api_id, $endpoint_id );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$stale_ttl` | `int` | Stale time to live in seconds. |
| `$api_id` | `string` | API identifier. |
| `$endpoint_id` | `string` | Endpoint identifier. |

<!-- example:huxx_connect/cache/stale_ttl -->
Adjust how long the stale-fallback copy of a response is retained. Return the (possibly changed) TTL in seconds; return the first argument unchanged to keep the resolved value.

```php
add_filter( 'huxx_connect/cache/stale_ttl', function ( $stale_ttl, $api_id, $endpoint_id ) {
	// Hold demo_api products for a full week as an outage safety net.
	if ( $api_id === 'demo_api' && $endpoint_id === 'get_products' ) {
		return WEEK_IN_SECONDS;
	}
	return $stale_ttl;
}, 10, 3 );
```

Result: the stale copy of `demo_api` / `get_products` survives for a week, so it can cover a long API outage.
<!-- /example:huxx_connect/cache/stale_ttl -->

### huxx_connect/cache/ttl

Filter the cache TTL.

**Type:** filter · **Since:** — · **Source:** `src/Cache/CacheManager.php`

```php
apply_filters( 'huxx_connect/cache/ttl', $ttl, $api_id, $endpoint_id );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$ttl` | `int` | Time to live in seconds. |
| `$api_id` | `string` | API identifier. |
| `$endpoint_id` | `string` | Endpoint identifier. |

<!-- example:huxx_connect/cache/ttl -->
Override the fresh-cache lifetime for a response about to be stored. Return the TTL in seconds; return the first argument unchanged to keep the endpoint's configured value.

```php
add_filter( 'huxx_connect/cache/ttl', function ( $ttl, $api_id, $endpoint_id ) {
	// Cache the products list for 15 minutes regardless of the endpoint setting.
	if ( $api_id === 'demo_api' && $endpoint_id === 'get_products' ) {
		return 15 * MINUTE_IN_SECONDS;
	}
	return $ttl;
}, 10, 3 );
```

Result: responses from `demo_api` / `get_products` are cached for 15 minutes, overriding whatever TTL the endpoint defines.
<!-- /example:huxx_connect/cache/ttl -->

## Display & Shortcodes

### huxx_connect/shortcode/allow_raw_html

Filter whether raw HTML is allowed in string shortcode output.

**Type:** filter · **Since:** — · **Source:** `src/Frontend/Shortcodes.php`

```php
apply_filters( 'huxx_connect/shortcode/allow_raw_html', $allow_raw, $atts );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$allow_raw` | `bool` | Whether to allow sanitized HTML output. |
| `$atts` | `array` | Parsed shortcode attributes. |

<!-- example:huxx_connect/shortcode/allow_raw_html -->
For string responses, controls whether the output is passed through `wp_kses_post()` (sanitized HTML) instead of being fully escaped inside a `<pre>` block. Default is `false`. Opt one endpoint in:

```php
add_filter( 'huxx_connect/shortcode/allow_raw_html', function ( bool $allow_raw, array $a ): bool {
	if ( 'demo_api' === $a['api_id'] && 'get_products' === $a['endpoint_id'] ) {
		return true;
	}

	return $allow_raw;
}, 10, 2 );
```

Result: a string response from that endpoint is rendered as sanitized HTML rather than escaped text in a `<pre>`. Only enable this for endpoints whose output you trust.
<!-- /example:huxx_connect/shortcode/allow_raw_html -->

### huxx_connect/shortcode/attributes

Filter the merged shortcode attributes before execution.

**Type:** filter · **Since:** — · **Source:** `src/Frontend/Shortcodes.php`

```php
apply_filters( 'huxx_connect/shortcode/attributes', $a, $atts );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$a` | `array` | The merged shortcode attributes. |
| `$atts` | `array|string` | The raw shortcode attributes as passed by WordPress. |

<!-- example:huxx_connect/shortcode/attributes -->
Rewrite the parsed attributes before the API call runs. Here every `demo_api`/`get_products` shortcode is defaulted to fetch 5 products unless the author already set `query_variables`.

```php
add_filter( 'huxx_connect/shortcode/attributes', function ( array $a, $atts ): array {
	if ( 'demo_api' === $a['api_id'] && 'get_products' === $a['endpoint_id'] && empty( $a['query_variables'] ) ) {
		$a['query_variables'] = 'limit=5';
	}

	return $a;
}, 10, 2 );
```

Result: `[huxx_api api_id="demo_api" endpoint_id="get_products"]` now requests `/products?limit=5`.
<!-- /example:huxx_connect/shortcode/attributes -->

### huxx_connect/shortcode/can_execute

Filter whether the shortcode should execute.

**Type:** filter · **Since:** — · **Source:** `src/Frontend/Shortcodes.php`

```php
apply_filters( 'huxx_connect/shortcode/can_execute', $can_execute, $a );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$can_execute` | `bool` | Whether execution is allowed. |
| `$a` | `array` | Parsed shortcode attributes. |

<!-- example:huxx_connect/shortcode/can_execute -->
Gate shortcode execution. Returning `false` makes `render()` short-circuit and output an empty string before any API call is made — here, restricting output to logged-in users.

```php
add_filter( 'huxx_connect/shortcode/can_execute', function ( bool $can_execute, array $a ): bool {
	if ( ! is_user_logged_in() ) {
		return false;
	}

	return $can_execute;
}, 10, 2 );
```

Result: guests see nothing; logged-in visitors get the normal API response.
<!-- /example:huxx_connect/shortcode/can_execute -->

### huxx_connect/shortcode/format_output

Filter the shortcode output after key extraction.

**Type:** filter · **Since:** — · **Source:** `src/Frontend/Shortcodes.php`

```php
apply_filters( 'huxx_connect/shortcode/format_output', $output, $result, $a );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$output` | `string|null` | Custom output, or null for default rendering. |
| `$result` | `mixed` | The API response data (after keys extraction). |
| `$a` | `array` | Parsed shortcode attributes. |

<!-- example:huxx_connect/shortcode/format_output -->
Return a non-null string to replace the default rendering (pretty-printed JSON in a `<pre>`). Return the incoming `$output` (null) to fall through to the default. Here the `demo_api` product array is rendered as a `<ul>`.

```php
add_filter( 'huxx_connect/shortcode/format_output', function ( $output, $result, array $a ) {
	if ( 'demo_api' !== $a['api_id'] || ! is_array( $result ) ) {
		return $output; // Fall through to default JSON rendering.
	}

	$items = '';
	foreach ( $result as $product ) {
		if ( isset( $product['title'] ) ) {
			$items .= '<li>' . esc_html( $product['title'] ) . '</li>';
		}
	}

	return '' !== $items ? '<ul class="product-list">' . $items . '</ul>' : $output;
}, 10, 3 );
```

Result:

```html
<ul class="product-list"><li>Essence Mascara Lash Princess</li><li>Eyeshadow Palette with Mirror</li></ul>
```
<!-- /example:huxx_connect/shortcode/format_output -->

## Import & Export

### huxx_connect/export/data

Filters the export data before it is returned.

**Type:** filter · **Since:** — · **Source:** `src/ImportExport/Exporter.php`

```php
apply_filters( 'huxx_connect/export/data', $export_data, $api_ids, $include_secrets );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$export_data` | `array` | The export data array. |
| `$api_ids` | `array<string>` | The API IDs being exported (empty for all). |
| `$include_secrets` | `bool` | Whether sensitive values are included. |

<!-- example:huxx_connect/export/data -->
Append a provenance stamp to every export payload, and note when secrets were withheld:

```php
add_filter( 'huxx_connect/export/data', function ( $export_data, $api_ids, $include_secrets ) {
    $export_data['exported_from'] = home_url();

    if ( ! $include_secrets ) {
        $export_data['note'] = 'Credentials were stripped from this export.';
    }

    return $export_data;
}, 10, 3 );
```

Result: the downloaded HuxxConnect JSON gains an `exported_from` key (and a `note` when the export was run without secrets).
<!-- /example:huxx_connect/export/data -->

### huxx_connect/import/formats

Filters the registry of importable formats.

**Type:** filter · **Since:** — · **Source:** `src/Admin/Rest/ImportExportController.php`

```php
apply_filters( 'huxx_connect/import/formats' );
```

<!-- example:huxx_connect/import/formats -->
Register a new importable format so the importer can detect it. Pair this with a `huxx_connect/import/parse` handler that does the actual parsing:

```php
add_filter( 'huxx_connect/import/formats', function ( $formats ) {
    $formats['insomnia'] = array(
        'label'        => 'Insomnia export',
        'detect'       => static function ( $d ) {
            return isset( $d['_type'] ) && 'export' === $d['_type'];
        },
        'requires_pro' => false,
        'builtin'      => false,
    );

    return $formats;
} );
```

Result: an uploaded file whose JSON has `"_type": "export"` is now recognized as the `insomnia` format.
<!-- /example:huxx_connect/import/formats -->

### huxx_connect/import/parse

Filters the parse of a non-native import format into HuxxConnect data.

**Type:** filter · **Since:** — · **Source:** `src/Admin/Rest/ImportExportController.php`

```php
apply_filters( 'huxx_connect/import/parse', $parsed, $format, $data );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$parsed` | `array|null` | Default null (no handler). |
| `$format` | `string` | Detected format slug. |
| `$data` | `mixed` | Decoded array or raw JSON/YAML string. |

<!-- example:huxx_connect/import/parse -->
Turn a detected non-native format into HuxxConnect import data. Return `null` (the passed-in `$parsed`) for formats you do not handle so other providers still get a turn:

```php
add_filter( 'huxx_connect/import/parse', function ( $parsed, $format, $data, $params ) {
    if ( 'insomnia' !== $format ) {
        return $parsed; // Not ours - pass through untouched.
    }

    return array(
        'success'  => true,
        'data'     => array( 'apis' => array() ), // ...map $data['resources'] into APIs/endpoints here.
        'warnings' => array(),
        'message'  => 'Imported from Insomnia.',
    );
}, 10, 4 );
```

Result: when the `insomnia` format is detected, the importer uses your returned `data` array instead of failing with an unknown-format error.
<!-- /example:huxx_connect/import/parse -->

### huxx_connect/import/warnings

Filters warnings generated during import.

**Type:** filter · **Since:** — · **Source:** `src/ImportExport/Importer.php`

```php
apply_filters( 'huxx_connect/import/warnings', $warnings, $data, $mode );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$warnings` | `string[]` | Warning messages. |
| `$data` | `array` | The raw import data. |
| `$mode` | `string` | The import mode ('merge', 'overwrite', or 'add_new'). |

<!-- example:huxx_connect/import/warnings -->
Append a human-readable warning to the import result when a known-fragile API is imported:

```php
add_filter( 'huxx_connect/import/warnings', function ( $warnings, $data, $mode ) {
    foreach ( $data['apis'] ?? array() as $api ) {
        if ( 'demo_api' === ( $api['api_id'] ?? '' ) ) {
            $warnings[] = 'Imported "demo_api" - set its base URL to https://dummyjson.com before use.';
        }
    }

    return $warnings;
}, 10, 3 );
```

Result: the extra string is surfaced alongside the import success message in the admin toast.
<!-- /example:huxx_connect/import/warnings -->

## Lifecycle & Data Events

### huxx_connect/debug_info

Filter debug info sections.

**Type:** filter · **Since:** — · **Source:** `src/Diagnostics/DebugExporter.php`

```php
apply_filters( 'huxx_connect/debug_info' );
```

<!-- example:huxx_connect/debug_info -->
Add your own section to the diagnostics export. Each section value must be an associative array of key => scalar/array pairs. Do not add credentials, keys, or tokens.

```php
add_filter( 'huxx_connect/debug_info', function ( $sections ) {
	$sections['my_integration'] = array(
		'Sync enabled' => get_option( 'my_plugin_sync_enabled' ) ? 'yes' : 'no',
		'Last sync'    => get_option( 'my_plugin_last_sync', 'never' ),
	);
	return $sections;
}, 10, 1 );
```

Result: the exported debug info gains a `my_integration` section alongside the built-in `plugin`, `wordpress`, and `server` sections.
<!-- /example:huxx_connect/debug_info -->

## Post Creator

### huxx_connect/post_creator/acf_value

Filter the coerced value before writing via ACF's update_field().

**Pro** · **Type:** filter · **Since:** — · **Source:** `src/Features/PostCreator/AcfWriteAdapter.php`

```php
apply_filters( 'huxx_connect/post_creator/acf_value', $value, $key, $field, $post_id );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$value` | `mixed` | The coerced value. |
| `$key` | `string` | The meta key / ACF field name. |
| `$field` | `array` | The full ACF field definition. |
| `$post_id` | `int` | The post ID being written to. |

<!-- example:huxx_connect/post_creator/acf_value -->
The ACF adapter has already coerced the value to the shape the field type expects; this filter is your last chance to adjust it before `update_field()` runs. Round a price to two decimals as it lands in an ACF number field:

```php
add_filter( 'huxx_connect/post_creator/acf_value', function ( $value, $key, $field, $post_id ) {
    if ( 'unit_price' === $key ) {
        return round( (float) $value, 2 );
    }
    return $value;
}, 10, 4 );
```

Result: a source value of `9.990000001` is stored on the ACF `unit_price` field as `9.99`.
<!-- /example:huxx_connect/post_creator/acf_value -->

### huxx_connect/post_creator/available_fields

Filter the available custom fields for a post type.

**Pro** · **Type:** filter · **Since:** — · **Source:** `src/Features/PostCreator/FieldDiscovery.php`

```php
apply_filters( 'huxx_connect/post_creator/available_fields', $fields, $post_type );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$fields` | `array` | Current field list. |
| `$post_type` | `string` | Post type slug. |

<!-- example:huxx_connect/post_creator/available_fields -->
Populates the mapping UI's custom-field autocomplete for a post type. Append a field your own code writes so it becomes selectable as a mapping target:

```php
add_filter( 'huxx_connect/post_creator/available_fields', function ( $fields, $post_type ) {
    if ( 'product' === $post_type ) {
        $fields[] = array(
            'name'   => 'msrp',
            'label'  => 'MSRP',
            'type'   => 'number',
            'source' => 'Custom',
        );
    }
    return $fields;
}, 10, 2 );
```

Result: an "MSRP" option appears in the field-mapping autocomplete when the Post Creator target post type is `product`.
<!-- /example:huxx_connect/post_creator/available_fields -->

### huxx_connect/post_creator/available_term_fields

Filter the available term-meta keys for a taxonomy.

**Pro** · **Type:** filter · **Since:** — · **Source:** `src/Features/PostCreator/TermFieldDiscovery.php`

```php
apply_filters( 'huxx_connect/post_creator/available_term_fields', $fields, $taxonomy );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$fields` | `array` | Discovered fields. Each entry: |
| `$taxonomy` | `string` | Taxonomy slug. |

<!-- example:huxx_connect/post_creator/available_term_fields -->
The taxonomy equivalent of `available_fields` — populates the term-meta autocomplete. Add a term-meta key for one taxonomy:

```php
add_filter( 'huxx_connect/post_creator/available_term_fields', function ( $fields, $taxonomy ) {
    if ( 'product_cat' === $taxonomy ) {
        $fields[] = array(
            'name'   => 'brand_logo',
            'label'  => 'Brand Logo',
            'source' => 'Custom',
        );
    }
    return $fields;
}, 10, 2 );
```

Result: "Brand Logo" becomes a selectable term-meta target for the `product_cat` taxonomy.
<!-- /example:huxx_connect/post_creator/available_term_fields -->

### huxx_connect/post_creator/duplicate_found

Filter the matched post ID when a duplicate is detected.

**Pro** · **Type:** filter · **Since:** 1.1.0 · **Source:** `src/Features/PostCreator/PostCreatorService.php`

```php
apply_filters( 'huxx_connect/post_creator/duplicate_found', $existing_id, $unique_value, $matched_via, $item, $config );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$existing_id` | `int` | The matched post ID. |
| `$unique_value` | `mixed` | The unique key value that matched. |
| `$matched_via` | `string` | How the match was found: 'unique_key' or 'matching'. |
| `$item` | `array` | The source API item. |
| `$config` | `PostCreatorConfig` | The Post Creator configuration. |

<!-- example:huxx_connect/post_creator/duplicate_found -->
When a match is found for an incoming item, this filter receives the matched post id. Return a different id to redirect the update, or `null` to force a fresh create instead. Here, a trashed match is bypassed so the item creates a new post rather than resurrecting the trashed one:

```php
add_filter( 'huxx_connect/post_creator/duplicate_found', function ( $existing_id, $unique_value, $matched_via, $item, $config ) {
    if ( 'trash' === get_post_status( $existing_id ) ) {
        return null; // Force creation instead of updating the trashed post.
    }
    return $existing_id;
}, 10, 5 );
```

Result: items whose only match is in the trash are created anew; all other matches update in place.
<!-- /example:huxx_connect/post_creator/duplicate_found -->

### huxx_connect/post_creator/etch_value

Filter the coerced value before writing via Etch's CustomFieldService::set_field_value().

**Pro** · **Type:** filter · **Since:** — · **Source:** `src/Features/PostCreator/EtchWriteAdapter.php`

```php
apply_filters( 'huxx_connect/post_creator/etch_value', $value, $key, $field, $post_id );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$value` | `mixed` | The coerced value. |
| `$key` | `string` | The Etch field key. |
| `$field` | `array` | The full Etch field definition. |
| `$post_id` | `int` | The post ID being written to. |

<!-- example:huxx_connect/post_creator/etch_value -->
Filters the coerced value just before it is written via Etch's `CustomFieldService::set_field_value()`. Map a numeric stock count to a human label for an Etch text field:

```php
add_filter( 'huxx_connect/post_creator/etch_value', function ( $value, $key, $field, $post_id ) {
    if ( 'availability' === $key ) {
        return ( (int) $value > 0 ) ? 'In stock' : 'Sold out';
    }
    return $value;
}, 10, 4 );
```

Result: a source `stock` of `0` writes `Sold out` into the Etch `availability` field; any positive count writes `In stock`.
<!-- /example:huxx_connect/post_creator/etch_value -->

### huxx_connect/post_creator/field_mapped

Filter the resolved taxonomy terms for a single field mapping.

**Pro** · **Type:** filter · **Since:** — · **Source:** `src/Features/PostCreator/FieldMapper.php`

```php
apply_filters( 'huxx_connect/post_creator/field_mapped', $terms, $target, $item, $mapping );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$terms` | `string[]` | Resolved term names. |
| `$target` | `string` | The target taxonomy identifier. |
| `$item` | `array` | The source API item. |
| `$mapping` | `array` | The full mapping entry. |

<!-- example:huxx_connect/post_creator/field_mapped -->
Fires for each individual field mapping, exposing the resolved value before it is assigned. `$mapping` is the full mapping entry, `$target` its destination. Title-case the mapped post title:

```php
add_filter( 'huxx_connect/post_creator/field_mapped', function ( $resolved, $target, $item, $mapping ) {
    if ( 'post_title' === $target ) {
        return ucwords( (string) $resolved );
    }
    return $resolved;
}, 10, 4 );
```

Result: a product title resolved as `essence mascara lash princess` is stored as `Essence Mascara Lash Princess`.
<!-- /example:huxx_connect/post_creator/field_mapped -->

### huxx_connect/post_creator/filter_rules

Filter the results-filter rules before they are applied.

**Pro** · **Type:** filter · **Since:** 1.1.0 · **Source:** `src/Features/PostCreator/PostCreatorService.php`

```php
apply_filters( 'huxx_connect/post_creator/filter_rules', $filter_rules, $items, $context );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$filter_rules` | `array` | Results filter rules config. |
| `$items` | `array` | Items about to be filtered (for inspection; do not modify). |
| `$context` | `array` | {@see HuxxConnectProSupportHookContext::build()}. |

<!-- example:huxx_connect/post_creator/filter_rules -->
Fires before the results-filter rules are applied, letting you amend the rule set. `$items` is provided for inspection only. The config shape is `{ enabled, logic, rules: [ { field, operator, value } ] }`. Add an in-stock-only rule:

```php
add_filter( 'huxx_connect/post_creator/filter_rules', function ( $filter_rules, $items, $context ) {
    $filter_rules['enabled'] = true;
    $filter_rules['rules'][] = array(
        'field'    => 'stock',
        'operator' => 'greater_than',
        'value'    => '0',
    );
    return $filter_rules;
}, 10, 3 );
```

Result: products with `stock` of `0` are filtered out before any posts are created.
<!-- /example:huxx_connect/post_creator/filter_rules -->

### huxx_connect/post_creator/filtered_items

Filter items after the results filter has been applied.

**Pro** · **Type:** filter · **Since:** 1.1.0 · **Source:** `src/Features/PostCreator/PostCreatorService.php`

```php
apply_filters( 'huxx_connect/post_creator/filtered_items', $items, $filter_config, $context );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$items` | `array[]` | Filtered items. |
| `$filter_config` | `array` | Rules that were just applied. |
| `$context` | `array` | {@see HuxxConnectProSupportHookContext::build()}. |

<!-- example:huxx_connect/post_creator/filtered_items -->
Fires after the results filter has run, on the surviving items. `$filter_config` is the rule set that was applied. Cap how many items a single run will process:

```php
add_filter( 'huxx_connect/post_creator/filtered_items', function ( $items, $filter_config, $context ) {
    return array_slice( $items, 0, 20 );
}, 10, 3 );
```

Result: even if the endpoint returns 100 products, at most the first 20 become posts on each run.
<!-- /example:huxx_connect/post_creator/filtered_items -->

### huxx_connect/post_creator/initial_post_data

Filter the initial WP post-data array BEFORE field mapping runs.

**Pro** · **Type:** filter · **Since:** 1.3.0 · **Source:** `src/Features/PostCreator/PostCreatorService.php`

```php
apply_filters( 'huxx_connect/post_creator/initial_post_data', $post_data, $item, $context );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$post_data` | `array` | Initial post data (empty by default). |
| `$item` | `array` | The source API item. |
| `$context` | `array` | {@see HuxxConnectProSupportHookContext::build()}. |

<!-- example:huxx_connect/post_creator/initial_post_data -->
Filters the post-data array BEFORE field mapping runs; mapped values are merged on top, so mapping wins on any key you set here. Use it to seed defaults the mapping UI doesn't expose:

```php
add_filter( 'huxx_connect/post_creator/initial_post_data', function ( $post_data, $item, $context ) {
    // Assign imported products to a fixed author unless a mapping overrides it.
    $post_data['post_author'] = 5;
    return $post_data;
}, 10, 3 );
```

Result: every created product post defaults to `post_author` 5.
<!-- /example:huxx_connect/post_creator/initial_post_data -->

### huxx_connect/post_creator/insert_post_data

Filter the post data array before a new post is inserted.

**Pro** · **Type:** filter · **Since:** 1.1.0 · **Source:** `src/Features/PostCreator/PostCreatorService.php`

```php
apply_filters( 'huxx_connect/post_creator/insert_post_data', $post_data, $item, $context );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$post_data` | `array` | The post data array for wp_insert_post(). |
| `$item` | `array` | The source API item being written. |
| `$context` | `array` | {@see HuxxConnectProSupportHookContext::build()}. |

<!-- example:huxx_connect/post_creator/insert_post_data -->
Filters the final post-data array right before `wp_insert_post()` for a NEW post (mapping has already run). Force out-of-stock products to import as drafts:

```php
add_filter( 'huxx_connect/post_creator/insert_post_data', function ( $post_data, $item, $context ) {
    if ( isset( $item['stock'] ) && 0 === (int) $item['stock'] ) {
        $post_data['post_status'] = 'draft';
    }
    return $post_data;
}, 10, 3 );
```

Result: a product with `stock` of `0` is inserted as a draft instead of published.
<!-- /example:huxx_connect/post_creator/insert_post_data -->

### huxx_connect/post_creator/item_hash

Filter the content hash computed for a source item.

**Pro** · **Type:** filter · **Since:** 1.3.4 · **Source:** `src/Features/PostCreator/PostCreatorService.php`

```php
apply_filters( 'huxx_connect/post_creator/item_hash', $hash, $item, $context );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$hash` | `string` | The computed sha256 hash. |
| `$item` | `array` | The source API item. |
| `$context` | `array` | {@see HuxxConnectProSupportHookContext::build()}. |

<!-- example:huxx_connect/post_creator/item_hash -->
```php
// The API includes a per-request timestamp that would make every item
// look changed. Hash the item without it.
add_filter( 'huxx_connect/post_creator/item_hash', function ( string $hash, array $item ) {
    unset( $item['fetched_at'], $item['view_count'] );
    return hash( 'sha256', wp_json_encode( $item ) );
}, 10, 2 );
```

With the volatile fields removed, unchanged products are skipped on every scheduled run instead of being rewritten because a timestamp moved.
<!-- /example:huxx_connect/post_creator/item_hash -->

### huxx_connect/post_creator/meta_box_value

Filter the coerced value before writing via rwmb_set_meta().

**Pro** · **Type:** filter · **Since:** — · **Source:** `src/Features/PostCreator/MetaBoxWriteAdapter.php`

```php
apply_filters( 'huxx_connect/post_creator/meta_box_value', $value, $key, $field, $post_id );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$value` | `mixed` | The coerced value. |
| `$key` | `string` | The meta key / Meta Box field id. |
| `$field` | `array` | The full Meta Box field definition. |
| `$post_id` | `int` | The post ID being written to. |

<!-- example:huxx_connect/post_creator/meta_box_value -->
The Meta Box counterpart to `acf_value` — filters the coerced value just before `rwmb_set_meta()`. Clamp a rating to one decimal place for a Meta Box number field:

```php
add_filter( 'huxx_connect/post_creator/meta_box_value', function ( $value, $key, $field, $post_id ) {
    if ( 'rating' === $key ) {
        return round( (float) $value, 1 );
    }
    return $value;
}, 10, 4 );
```

Result: a source rating of `4.37` is stored on the Meta Box `rating` field as `4.4`.
<!-- /example:huxx_connect/post_creator/meta_box_value -->

### huxx_connect/post_creator/protected_meta_keys

Filter the protected meta-key prefixes and exact keys Post Creator refuses to overwrite.

**Pro** · **Type:** filter · **Since:** — · **Source:** `src/Features/PostCreator/PostCreatorService.php`

```php
apply_filters( 'huxx_connect/post_creator/protected_meta_keys', $protected );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$protected` | `array` | Map with `prefixes` and `exact` key lists. |

<!-- example:huxx_connect/post_creator/protected_meta_keys -->
Filters the map of meta keys Post Creator refuses to overwrite. `$protected` has two lists: `prefixes` and `exact`. Add a prefix so no mapping can ever touch your account-internal meta:

```php
add_filter( 'huxx_connect/post_creator/protected_meta_keys', function ( $protected ) {
    $protected['prefixes'][] = '_acct_';
    return $protected;
} );
```

Result: a mapping targeting `_acct_balance` is silently skipped rather than overwritten.
<!-- /example:huxx_connect/post_creator/protected_meta_keys -->

### huxx_connect/post_creator/protected_post_types

Filter the protected post types Post Creator refuses to write to.

**Pro** · **Type:** filter · **Since:** — · **Source:** `src/Features/PostCreator/PostCreatorConfig.php`

```php
apply_filters( 'huxx_connect/post_creator/protected_post_types', $post_types );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$post_types` | `string[]` | Protected post type slugs. |

<!-- example:huxx_connect/post_creator/protected_post_types -->
Filters the list of post types Post Creator will not write to. Add your own CPT to the blocklist:

```php
add_filter( 'huxx_connect/post_creator/protected_post_types', function ( $post_types ) {
    $post_types[] = 'invoice';
    return $post_types;
} );
```

Result: the `invoice` post type is refused as a Post Creator target, alongside the built-in protected types (attachment, revision, etc.).
<!-- /example:huxx_connect/post_creator/protected_post_types -->

### huxx_connect/post_creator/should_update

Filter whether an existing post should be rewritten.

**Pro** · **Type:** filter · **Since:** 1.3.4 · **Source:** `src/Features/PostCreator/PostCreatorService.php`

```php
apply_filters( 'huxx_connect/post_creator/should_update', $should_update, $existing_id, $content_hash, $stored_hash, $item, $context );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$should_update` | `bool` | Whether to proceed with the update. |
| `$existing_id` | `int` | The matched post ID. |
| `$content_hash` | `string` | Hash of the current source item. |
| `$stored_hash` | `string` | Hash stored on the last write ('' if none). |
| `$item` | `array` | The source API item. |
| `$context` | `array` | {@see HuxxConnectProSupportHookContext::build()}. |

<!-- example:huxx_connect/post_creator/should_update -->
```php
// Force a rewrite of one specific item even when its hash matches.
add_filter( 'huxx_connect/post_creator/should_update', function ( bool $should_update, int $existing_id, string $content_hash, string $stored_hash, array $item ) {
    if ( isset( $item['id'] ) && 42 === (int) $item['id'] ) {
        return true;
    }
    return $should_update;
}, 10, 5 );
```

Returning `false` skips the write; the run summary counts the item as skipped with the reason "Content unchanged (hash match)."
<!-- /example:huxx_connect/post_creator/should_update -->

### huxx_connect/post_creator/source_items

Filter items before field mapping and post creation.

**Pro** · **Type:** filter · **Since:** 1.1.0 · **Source:** `src/Features/PostCreator/PostCreatorService.php`

```php
apply_filters( 'huxx_connect/post_creator/source_items', $items, $context );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$items` | `array[]` | Items from the API source. |
| `$context` | `array` | {@see HuxxConnectProSupportHookContext::build()}. |

<!-- example:huxx_connect/post_creator/source_items -->
Fires once, on the full item list, before field mapping and post creation — the place to enrich or prune the feed. Drop items missing a price so they never become posts:

```php
add_filter( 'huxx_connect/post_creator/source_items', function ( $items, $context ) {
    return array_values( array_filter( $items, function ( $item ) {
        return ! empty( $item['price'] );
    } ) );
}, 10, 2 );
```

Result: products from `demo_api:get_products` with no `price` are removed before any post is created.
<!-- /example:huxx_connect/post_creator/source_items -->

### huxx_connect/post_creator/update_post_data

Filter the post data array before an existing post is updated.

**Pro** · **Type:** filter · **Since:** 1.1.0 · **Source:** `src/Features/PostCreator/PostCreatorService.php`

```php
apply_filters( 'huxx_connect/post_creator/update_post_data', $post_data, $post_id, $item, $context );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$post_data` | `array` | The post data array for wp_update_post(). |
| `$post_id` | `int` | The post being updated. |
| `$item` | `array` | The source API item. |
| `$context` | `array` | {@see HuxxConnectProSupportHookContext::build()}. |

<!-- example:huxx_connect/post_creator/update_post_data -->
Filters the post-data array before `wp_update_post()` for an EXISTING post (mapping has already run, and `ID` is already present). Preserve manual title edits by never overwriting the title on updates:

```php
add_filter( 'huxx_connect/post_creator/update_post_data', function ( $post_data, $post_id, $item, $context ) {
    unset( $post_data['post_title'] );
    return $post_data;
}, 10, 4 );
```

Result: re-syncing a product refreshes its mapped fields but leaves an editor's hand-tweaked `post_title` intact.
<!-- /example:huxx_connect/post_creator/update_post_data -->

### huxx_connect/post_creator/write_meta

Filter: allow field plugin adapters (ACF, MetaBox, Pods) to handle the meta write using their own API. Return true to indicate the write was handled; false to fall through to update_post_meta().

**Pro** · **Type:** filter · **Since:** — · **Source:** `src/Features/PostCreator/PostCreatorService.php`

```php
apply_filters( 'huxx_connect/post_creator/write_meta', $handled, $post_id, $key, $value, $options );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$handled` | `bool` | Whether the write was handled. |
| `$post_id` | `int` | Post ID. |
| `$key` | `string` | Meta key. |
| `$value` | `mixed` | Meta value. |
| `$options` | `array` | Per-mapping options (e.g., image_source for |

<!-- example:huxx_connect/post_creator/write_meta -->
Lets a field-plugin adapter claim a meta write and route it through its own API. Return `true` to signal the write was handled (Post Creator then skips `update_post_meta()`); return the incoming `$handled` to fall through. Route one key through Pods:

```php
add_filter( 'huxx_connect/post_creator/write_meta', function ( $handled, $post_id, $key, $value, $options ) {
    if ( $handled || 'specs' !== $key || ! function_exists( 'pods' ) ) {
        return $handled;
    }
    pods( get_post_type( $post_id ), $post_id )->save( $key, $value );
    return true; // Handled — Post Creator skips its own meta write.
}, 10, 5 );
```

Result: the `specs` field is persisted via Pods' API instead of raw `update_post_meta()`; all other keys fall through untouched.
<!-- /example:huxx_connect/post_creator/write_meta -->

## Builder Sync

### huxx_connect/builder_sync/items_for_builder

Filter the shaped items for ONE builder just before its write_loop() call. Unlike `source_items` (which runs once and feeds every builder), this runs per builder — use it for builder-specific VALUE transforms (e.g. Etch image formatting, ClickUp 86ca888t3) that must not leak into other builders' storage. The shared $items stay untouched.

**Pro** · **Type:** filter · **Since:** 1.3.4 · **Source:** `src/Features/BuilderSync/BuilderSyncService.php`

```php
apply_filters( 'huxx_connect/builder_sync/items_for_builder', $items, $builder_id, $context );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$items` | `array[]` | Shaped items ready for this builder. |
| `$builder_id` | `string` | Builder identifier (e.g. 'etch', 'bricks'). |
| `$context` | `array` | Same shape as `source_items` context. |

<!-- example:huxx_connect/builder_sync/items_for_builder -->
Runs per builder, just before that builder's `write_loop()` call — use it for value transforms that must not leak into other builders' storage. The callback must return a sequential list (an associative result is discarded and the unfiltered items are used instead).

```php
add_filter(
    'huxx_connect/builder_sync/items_for_builder',
    function ( array $items, string $builder_id, array $context ) {
        // Only rewrite the value for Etch — Bricks and others keep
        // the raw thumbnail URL untouched.
        if ( 'etch' !== $builder_id ) {
            return $items;
        }

        foreach ( $items as $i => $item ) {
            if ( ! empty( $item['thumbnail'] ) ) {
                $items[ $i ]['thumbnail_html'] = '<img src="' . esc_url( $item['thumbnail'] ) . '" alt="" />';
            }
        }

        return $items; // stays a sequential list
    },
    10,
    3
);
```

Result: Etch loop items gain a `thumbnail_html` field wrapping the image URL in an `<img>` tag; every other builder receives the shared items with only the plain `thumbnail` URL.
<!-- /example:huxx_connect/builder_sync/items_for_builder -->

### huxx_connect/builder_sync/source_items

This filter is documented in BuilderSyncService::sync_collection().

**Pro** · **Type:** filter · **Since:** — · **Source:** `src/Features/BuilderSync/SingleItemProvider.php`

```php
apply_filters( 'huxx_connect/builder_sync/source_items' );
```

<!-- example:huxx_connect/builder_sync/source_items -->
Runs once per collection sync, after data shaping and results filtering, and the returned items feed every active builder. Add a computed field to each item here so all builders receive it.

```php
add_filter(
    'huxx_connect/builder_sync/source_items',
    function ( array $items, array $context ) {
        // demo_api / get_products returns dummyjson products with
        // `price` and `discountPercentage`. Add a `final_price` field.
        foreach ( $items as $i => $item ) {
            $price    = isset( $item['price'] ) ? (float) $item['price'] : 0.0;
            $discount = isset( $item['discountPercentage'] ) ? (float) $item['discountPercentage'] : 0.0;

            $items[ $i ]['final_price'] = round( $price * ( 1 - $discount / 100 ), 2 );
        }

        return $items;
    },
    10,
    2
);
```

Result: every synced product gains a `final_price` field (e.g. `price` 9.99 with a 12% discount becomes `final_price` 8.79), available to every active builder's loop.
<!-- /example:huxx_connect/builder_sync/source_items -->

## OAuth

### huxx_connect/oauth/presets

Filter the available OAuth provider presets.

**Pro** · **Type:** filter · **Since:** — · **Source:** `src/Features/OAuth/OAuthPresets.php`

```php
apply_filters( 'huxx_connect/oauth/presets' );
```

<!-- example:huxx_connect/oauth/presets -->
Register an OAuth provider preset so it appears in the auth-config picker with its endpoints pre-filled. `$presets` is keyed by slug; return the array with your entry added.

```php
add_filter( 'huxx_connect/oauth/presets', function ( $presets ) {
	$presets['acme'] = array(
		'label'      => 'Acme CRM',
		'auth_url'   => 'https://auth.acme.example/oauth/authorize',
		'token_url'  => 'https://auth.acme.example/oauth/token',
		'scopes'     => 'contacts.read',
		'pkce'       => true,
		'grant_type' => 'authorization_code',
		'help'       => 'Create an app in the Acme developer console and add your site callback URL.',
	);

	return $presets;
} );
```

Result: an "Acme CRM" option is added alongside the built-in presets (Google, GitHub, HubSpot, …); selecting it auto-fills the auth and token URLs, scopes, and PKCE setting.
<!-- /example:huxx_connect/oauth/presets -->

## HTML Formatter

### huxx_connect_pro/html_formatter/allow_raw_value

Filter whether raw HTML is allowed for a specific key's value.

**Pro** · **Type:** filter · **Since:** — · **Source:** `src/Features/HtmlFormatter/HtmlFormatterService.php`

```php
apply_filters( 'huxx_connect_pro/html_formatter/allow_raw_value', $allow, $key, $value, $atts );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$allow` | `bool` | Whether to allow sanitized HTML output. |
| `$key` | `string` | The array key name. |
| `$value` | `string` | The raw value. |
| `$atts` | `array` | Parsed shortcode attributes (not available here — passed as empty). |

<!-- example:huxx_connect_pro/html_formatter/allow_raw_value -->
```php
// The demo_api /products response has an HTML-ish `description`. By default the
// formatter escapes every value with esc_html(); returning true here routes this
// one key through wp_kses_post() instead, so safe inline markup survives.
add_filter(
	'huxx_connect_pro/html_formatter/allow_raw_value',
	function ( bool $allow, string $key, string $value ): bool {
		if ( 'description' === $key ) {
			return true;
		}
		return $allow;
	},
	10,
	3
);
```

Result: `<div class="huxx-connect-description">A <strong>bold</strong> phone</div>` — the `<strong>` is kept, while a `<script>` in the value would still be stripped by `wp_kses_post()`.

The filter passes three arguments (`$allow`, `$key`, `$value`), so register with `10, 3`.
<!-- /example:huxx_connect_pro/html_formatter/allow_raw_value -->

### huxx_connect_pro/html_formatter/output

Filter the final HTML formatter output.

**Pro** · **Type:** filter · **Since:** — · **Source:** `src/Features/HtmlFormatter/HtmlFormatterService.php`

```php
apply_filters( 'huxx_connect_pro/html_formatter/output', $html, $data, $atts );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$html` | `string` | The complete HTML output. |
| `$data` | `array` | The original API response data. |
| `$atts` | `array` | Parsed shortcode attributes. |

<!-- example:huxx_connect_pro/html_formatter/output -->
Wrap the finished `[huxx_connect]` HTML output in a themed container. `$html` is the complete markup, `$data` the original API response array, and `$atts` the parsed shortcode attributes.

```php
add_filter(
	'huxx_connect_pro/html_formatter/output',
	function ( string $html, array $data, array $atts ): string {
		if ( ( $atts['endpoint_id'] ?? '' ) !== 'get_products' ) {
			return $html;
		}
		return '<section class="product-card">' . $html . '</section>';
	},
	10,
	3
);
```

Result: output from the `demo_api` `get_products` endpoint is now wrapped in `<section class="product-card">…</section>`.
<!-- /example:huxx_connect_pro/html_formatter/output -->

## Pagination

### huxx_connect/pagination/delay

Filter the inter-page delay in seconds.

**Pro** · **Type:** filter · **Since:** — · **Source:** `src/Services/PaginatedFetcher.php`

```php
apply_filters( 'huxx_connect/pagination/delay', $seconds, $api_id, $endpoint_id, $page_number );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$seconds` | `float` | Seconds to sleep before the next request. |
| `$api_id` | `string` | API identifier. |
| `$endpoint_id` | `string` | Endpoint identifier. |
| `$page_number` | `int` | 1-indexed page number just fetched. |

<!-- example:huxx_connect/pagination/delay -->
Throttle the paginated fetch by sleeping between page requests so you stay under an upstream rate limit. The return is cast to `float`; `0` (the default) means no delay. This filter runs only when another page will actually be fetched.

```php
add_filter(
	'huxx_connect/pagination/delay',
	function ( $seconds, $api_id, $endpoint_id, $page_number ) {
		if ( 'demo_api' === $api_id && 'get_products' === $endpoint_id ) {
			return 0.5; // wait half a second before each subsequent page.
		}
		return $seconds;
	},
	10,
	4
);
```

Result: HuxxConnect pauses 0.5s between successive `get_products` page requests; all other endpoints keep the default (no delay).
<!-- /example:huxx_connect/pagination/delay -->

### huxx_connect/pagination/next_value

Filter the next page value for pagination.

**Pro** · **Type:** filter · **Since:** — · **Source:** `src/Services/PaginatedFetcher.php`

```php
apply_filters( 'huxx_connect/pagination/next_value', $next_value, $response, $pagination, $page_number, $api_id, $endpoint_id );
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `$next_value` | `mixed` | The calculated next value (or null to stop). |
| `$response` | `mixed` | The full API response body. |
| `$pagination` | `PaginationConfig` | The pagination configuration. |
| `$page_number` | `int` | 1-indexed page number just fetched. |
| `$api_id` | `string` | API identifier (added v1.3.1 — the |
| `$endpoint_id` | `string` | Endpoint identifier (added v1.3.1). |

<!-- example:huxx_connect/pagination/next_value -->
Stop paginating early once the upstream response signals you have collected enough. `demo_api` / `get_products` returns `skip` and `total` on each page, so you can cap the walk at 100 products. Return `false` (or `null`) to halt; return `$next_value` unchanged to continue.

```php
add_filter(
	'huxx_connect/pagination/next_value',
	function ( $next_value, $response, $pagination, $page_number, $api_id, $endpoint_id ) {
		if ( 'demo_api' !== $api_id || 'get_products' !== $endpoint_id ) {
			return $next_value;
		}
		if ( is_array( $response ) && isset( $response['skip'], $response['total'] ) ) {
			if ( $response['skip'] + 30 >= min( 100, (int) $response['total'] ) ) {
				return false; // false stops pagination.
			}
		}
		return $next_value;
	},
	10,
	6
);
```

Result: the fetch halts after the page that reaches 100 accumulated items instead of walking the full `total`.
<!-- /example:huxx_connect/pagination/next_value -->

