> ## Documentation Index
> Fetch the complete documentation index at: https://agenticadvertisingorg-addie-wg-slack-context.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# get_creative_features

> get_creative_features evaluates a creative manifest against a governance agent and returns feature values for brand safety and compliance in AdCP.

# get\_creative\_features

Evaluates a creative manifest and returns feature values from a creative governance agent.

## Use cases

* **Security scanning**: Detect malware, auto-redirects, credential harvesting, cloaking
* **Creative quality**: Evaluate brand consistency, platform optimization, guideline adherence
* **Content categorization**: Classify creative content against IAB Content Taxonomy or other standards
* **Accessibility**: Check WCAG compliance, screen reader compatibility

## Request

```json theme={null}
{
  "$schema": "/schemas/3.2.0-rc.6/creative/get-creative-features-request.json",
  "idempotency_key": "550e8400-e29b-41d4-a716-446655440760",
  "creative_manifest": {
    "format_kind": "display_tag",
    "assets": {
      "tag_url": {
        "asset_type": "url",
        "url": "https://cdn.agency.com/creative/abc123.html"
      }
    }
  },
  "feature_ids": ["auto_redirect", "credential_harvest", "cloaking"]
}
```

### Parameters

| Parameter                  | Type      | Required                            | Description                                                                                                                         |
| -------------------------- | --------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `idempotency_key`          | string    | No (SHOULD in 3.x; required in 4.0) | Client-generated UUID for this logical evaluation. Exact retries reuse the same key and canonical payload.                          |
| `creative_manifest`        | object    | Yes                                 | Canonical creative manifest with `format_kind`, optional `format_option_ref`, and typed `assets`                                    |
| `feature_ids`              | string\[] | No                                  | Filter to specific features. If omitted, evaluates all features the agent supports.                                                 |
| `account`                  | object    | Conditional                         | Account used to scope the evaluation and any provider billing. Required when the provider charges per evaluation.                   |
| `push_notification_config` | object    | No                                  | Webhook registration for terminal delivery when the provider returns `status: "submitted"`. Polling by `task_id` remains available. |

### Retry and reconciliation contract

`get_creative_features` can allocate provider compute, an evaluation record, consumption, and cost metadata even when the caller-facing price is zero. To preserve 3.x wire compatibility, `idempotency_key` is optional until AdCP 4.0, but clients SHOULD send one for every logical evaluation. When a key is supplied to a provider that advertises replay support, the provider MUST retain its replay record for at least 24 hours. An unkeyed 3.x request remains valid, but cannot be retried safely after an ambiguous result.

* With a supplied key, the same key and the same RFC 8785 canonical payload returns the cached initial response with `replayed: true`. The provider performs no second evaluation and records no second consumption or charge.
* Reusing a supplied key with a different `creative_manifest`, `account`, or `feature_ids` payload returns [`IDEMPOTENCY_CONFLICT`](/dist/docs/3.2.0-rc.6/building/by-layer/L3/error-handling#idempotency-errors). Array order remains significant; `feature_ids` does not receive set-like canonicalization.
* For a keyed request, `context` is opaque echo data: it is excluded from the canonical replay hash and MUST NOT be used as a replay fence. Changing only `context` does not authorize a new evaluation.
* An in-flight keyed duplicate follows the provider's declared wait-and-replay policy or returns [`IDEMPOTENCY_IN_FLIGHT`](/dist/docs/3.2.0-rc.6/building/by-layer/L3/error-handling#idempotency-errors). Callers retry with the same key after `error.retry_after`; they MUST NOT mint a new key.
* Replaying a keyed async request returns the original submitted acknowledgement with the same `task_id` and, when present, the same `evaluation_id`. Poll `task_id` for current state.

Providers advertise replay support through `adcp.idempotency` in [`get_adcp_capabilities`](/dist/docs/3.2.0-rc.6/protocol/get_adcp_capabilities). A client MUST treat an omitted key, `supported: false`, or a replay window below 24 hours as unsafe for automatic `get_creative_features` retry. After an ambiguous timeout it must fail closed and reconcile out of band rather than minting a new key.

### Authentication when used as a build evaluator

`get_creative_features` is also the evaluator contract used when `build_creative.evaluator.agent_url` or `build_creative.evaluator.feature_agent.agent_url` points at an external evaluator. In that flow, the creative/seller agent is the caller and the evaluator authenticates it on the transport: RFC 9421 request signing with JWKS discovery is preferred, and mTLS or a pre-provisioned Bearer/API-key credential are acceptable when both parties have arranged them.

The request payload is not an authentication channel. Evaluators MUST NOT treat `agent_url`, `account`, `context`, `ext`, or fields inside `creative_manifest` as proof of caller identity, and callers MUST NOT put evaluator credentials or caller-supplied trust material in those fields. Credential- or trust-material payload keys such as `api_key`, `client_secret`, `bearer`, `authorization`, `jwk`, `jwks`, or `jwks_uri` are non-conforming and should be rejected with [`CREDENTIAL_IN_ARGS`](/dist/docs/3.2.0-rc.6/building/by-layer/L3/error-handling#authentication-and-access).

After authenticating the transport, the evaluator maps the caller to its allowed creative agent/account configuration. A request signed by, or carrying credentials for, an unrecognized creative agent should fail authentication or authorization; it should not be accepted because the payload names an expected account.

When this call was initiated by [`build_creative`](/dist/docs/3.2.0-rc.6/creative/task-reference/build_creative) and the evaluator is unreachable or rejects the producing agent's transport authentication, the buyer-visible build should fall back to seller-default ranking with an advisory `errors[]` note rather than failing solely because evaluation was unavailable.

## Response

<CodeGroup>
  ```json Security scanner (clean) theme={null}
  {
    "$schema": "/schemas/3.2.0-rc.6/creative/get-creative-features-response.json",
    "status": "completed",
    "evaluation_id": "eval_clean_abc123",
    "results": [
      { "feature_id": "auto_redirect", "value": false },
      { "feature_id": "credential_harvest", "value": false },
      { "feature_id": "cloaking", "value": false }
    ],
    "detail_url": "https://scanner.example.com/reports/ctx_abc123"
  }
  ```

  ```json Security scanner (threat detected) theme={null}
  {
    "$schema": "/schemas/3.2.0-rc.6/creative/get-creative-features-response.json",
    "status": "completed",
    "evaluation_id": "eval_threat_def456",
    "results": [
      { "feature_id": "auto_redirect", "value": true, "confidence": 0.97 },
      { "feature_id": "credential_harvest", "value": true, "confidence": 0.91 },
      { "feature_id": "cloaking", "value": false }
    ],
    "detail_url": "https://scanner.example.com/reports/ctx_def456"
  }
  ```

  ```json Creative quality platform theme={null}
  {
    "$schema": "/schemas/3.2.0-rc.6/creative/get-creative-features-response.json",
    "status": "completed",
    "evaluation_id": "eval_quality_ghi789",
    "results": [
      { "feature_id": "brand_consistency", "value": 87, "unit": "percentage" },
      { "feature_id": "platform_optimized", "value": true },
      { "feature_id": "creative_quality_score", "value": 92, "unit": "score" }
    ],
    "detail_url": "https://quality.example.com/reports/ctx_ghi789"
  }
  ```

  ```json Content categorizer theme={null}
  {
    "$schema": "/schemas/3.2.0-rc.6/creative/get-creative-features-response.json",
    "status": "completed",
    "evaluation_id": "eval_category_jkl012",
    "results": [
      { "feature_id": "iab_casinos_gambling", "value": true, "confidence": 0.95 },
      { "feature_id": "iab_automotive", "value": false, "confidence": 0.12 }
    ],
    "detail_url": "https://categorizer.example.com/reports/ctx_jkl012"
  }
  ```

  ```json Provenance audit observation theme={null}
  {
    "$schema": "/schemas/3.2.0-rc.6/creative/get-creative-features-response.json",
    "status": "completed",
    "evaluation_id": "eval_provenance_mno345",
    "results": [
      { "feature_id": "ai_generated", "value": true, "confidence": 0.94 }
    ],
    "audit_observations": [
      {
        "code": "OVERSIGHT_DISCLOSURE_CARVEOUT_CLAIMED",
        "severity": "audit-worthy",
        "recovery": "informational",
        "field": "creative_manifest.provenance.disclosure.required",
        "message": "Creative claims human-directed AI output does not require disclosure; retain for audit review.",
        "details": {
          "agent_url": "https://governance.encypher.seller.example",
          "feature_id": "ai_generated",
          "claimed_value": {
            "human_oversight": "directed",
            "disclosure_required": false
          },
          "observed_value": true,
          "confidence": 0.94
        }
      }
    ]
  }
  ```

  ```json Submitted evaluation theme={null}
  {
    "$schema": "/schemas/3.2.0-rc.6/creative/get-creative-features-response.json",
    "status": "submitted",
    "task_id": "task_eval_abc123",
    "evaluation_id": "eval_async_abc123",
    "message": "Sandboxed scan accepted and queued"
  }
  ```
</CodeGroup>

### Response fields

| Field                                        | Description                                                                                                                                                                                                    |
| -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `evaluation_id`                              | Provider-generated evaluation identity. Optional in 3.x (providers SHOULD emit it) and required in 4.0. When present, stable across exact replays and async completion.                                        |
| `task_id`                                    | AdCP task identity on a submitted acknowledgement. Use it to poll current state; it is distinct from `evaluation_id`.                                                                                          |
| `results`                                    | Array of feature evaluation results                                                                                                                                                                            |
| `results[].feature_id`                       | Which feature was evaluated                                                                                                                                                                                    |
| `results[].value`                            | Feature value: boolean (binary), number (quantitative), or string (categorical)                                                                                                                                |
| `results[].confidence`                       | Confidence score (0-1), when applicable                                                                                                                                                                        |
| `results[].unit`                             | Unit for quantitative values (e.g., `percentage`, `score`)                                                                                                                                                     |
| `results[].expires_at`                       | When this evaluation expires and should be refreshed                                                                                                                                                           |
| `results[].measured_at`                      | When this feature was evaluated                                                                                                                                                                                |
| `results[].methodology_version`              | Version of methodology used                                                                                                                                                                                    |
| `results[].details`                          | Vendor-specific details                                                                                                                                                                                        |
| `detail_url`                                 | URL to vendor's full assessment. Access-controlled by the vendor.                                                                                                                                              |
| `audit_observations[]`                       | Non-blocking observations for audit routing. These are not rejection signals by themselves.                                                                                                                    |
| `audit_observations[].code`                  | Machine-readable observation code, such as `OVERSIGHT_DISCLOSURE_CARVEOUT_CLAIMED`                                                                                                                             |
| `audit_observations[].severity`              | Routing severity; currently `audit-worthy`                                                                                                                                                                     |
| `audit_observations[].recovery`              | Caller recovery category; currently `informational`                                                                                                                                                            |
| `audit_observations[].field`                 | Resolved creative manifest path for the risky claim side of the observation. Multi-field observations anchor the primary claim path, not necessarily every trigger field.                                      |
| `audit_observations[].message`               | Human-readable audit queue summary; do not include PII, cross-tenant data, or vendor-only report details                                                                                                       |
| `audit_observations[].details`               | Audit-safe details limited to `{ agent_url, feature_id, claimed_value, observed_value, confidence, substituted_for }`                                                                                          |
| `audit_observations[].details.claimed_value` | For `OVERSIGHT_DISCLOSURE_CARVEOUT_CLAIMED`, a compact object `{ human_oversight, disclosure_required }`; `disclosure_required` is the flattened alias for `creative_manifest.provenance.disclosure.required`. |

`feature_ids` filters `results[]`; `audit_observations[]` may still appear when the submitted provenance itself is audit-worthy. `OVERSIGHT_DISCLOSURE_CARVEOUT_CLAIMED` fires when `human_oversight` is `edited` or `directed` and `disclosure.required` is `false`; verifier observations such as `observed_value` and `confidence` are optional audit context.

### Error response

```json theme={null}
{
  "errors": [
    {
      "code": "CREATIVE_INACCESSIBLE",
      "message": "Could not retrieve creative assets for evaluation"
    }
  ]
}
```

## Async evaluation

Some evaluations (for example, sandboxed malware scanning) take time. After accepting the work, the provider returns `status: "submitted"` and a pollable `task_id`. In 3.x, providers SHOULD also include the provider-generated `evaluation_id`; it becomes required in 4.0. The caller can always poll [`get_task_status`](https://adcontextprotocol.org/schemas/3.2.0-rc.6/protocol/get-task-status-request.json); when the request included `push_notification_config`, the provider also delivers at least the terminal completion or failure notification. When the acknowledgement includes `evaluation_id`, the terminal success artifact MUST repeat it. Any `pricing_option_id`, `vendor_cost`, `currency`, and `consumption` values are part of that terminal artifact. `detail_url` is optional and is not an evaluation identity or reconciliation key.

An exact keyed replay while the evaluation remains async returns the original submitted acknowledgement with `replayed: true`, the same `task_id`, and, when present, the same `evaluation_id`; it does not return current task state. Poll the task for current state. This uses the standard [async task pattern](/dist/docs/3.2.0-rc.6/building/by-layer/L3/async-operations).

## Orchestrator logic

The orchestrator applies feature requirements on the client side, the same way property list feature requirements work:

```javascript theme={null}
const result = await agent.getCreativeFeatures({
  idempotency_key: crypto.randomUUID(),
  creative_manifest: manifest
});

if (result.errors) {
  // Handle error - reject or retry
  return;
}

if (result.status === 'submitted') {
  const evaluation = result.evaluation_id ?? 'provider did not return an evaluation ID';
  console.log(`Evaluation ${evaluation} queued as task ${result.task_id}`);
  return;
}

// Apply security requirements
const threats = result.results.filter(
  f => ['auto_redirect', 'credential_harvest', 'cloaking'].includes(f.feature_id)
    && f.value === true
);
if (threats.length > 0) {
  // Reject - security threat detected
  return;
}

// Apply quality requirements
const quality = result.results.find(f => f.feature_id === 'brand_consistency');
if (quality && quality.value < 80) {
  // Reject - below quality threshold
  return;
}

for (const observation of result.audit_observations ?? []) {
  if (observation.severity === 'audit-worthy') {
    // Retain or route for review; do not reject solely on this signal.
    recordAuditObservation(observation);
  }
}
```

## Relationship to property governance

Creative governance follows the same pattern as property governance:

| Concept                   | Property governance                     | Creative governance                                |
| ------------------------- | --------------------------------------- | -------------------------------------------------- |
| **What's evaluated**      | Properties (websites, apps)             | Creatives (manifests)                              |
| **Feature declarations**  | `governance.property_features`          | `governance.creative_features`                     |
| **Evaluation task**       | Property list filters                   | `get_creative_features`                            |
| **Feature values**        | `property-feature-value` schema         | Same fields (value, confidence, expires\_at, etc.) |
| **Detailed intelligence** | Behind `detail_url` / `methodology_url` | Behind `detail_url` / `methodology_url`            |
