> ## Documentation Index
> Fetch the complete documentation index at: https://docs.opal.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Give Paladin extra context with context provider scripts

> Write Paladin context scripts in OpalScript to pull data from your own systems into a Paladin review.

export const SectionHeader = ({children}) => {
  return <div style={{
    fontWeight: "bold",
    borderBottom: "1px solid #e5e7eb",
    paddingBottom: "0.25rem",
    marginBottom: "0.5rem"
  }}>
      {children}
    </div>;
};

<Note>
  Paladin context scripts are currently in beta. Contact Opal support to enable them for your organization.
</Note>

A context provider script (shown as **Paladin context** in the editor) fetches data from your own systems and hands it to a [Paladin](/docs/paladin/overview) agent as extra context for a review. Use it when the signal Paladin needs lives somewhere a built-in connector doesn't reach. Common examples are device posture, identity-provider account status and MFA enrollment, or recent activity in a tool your team uses.

A context provider script never approves, denies, or comments. It only adds context. Paladin weighs that context alongside everything else it gathers and still makes the decision.

<Info>
  Context provider scripts fail open. If a script errors, times out, or adds nothing, Paladin simply makes its decision without that context. A broken script never blocks a review.
</Info>

## How it runs

1. A request is routed to a Paladin agent that has the script added as a context source.
2. When the agent starts its review, Opal runs each of the agent's context scripts against the request. Scripts run in parallel.
3. Every `actions.add_context(...)` call the script makes becomes a context item that Paladin reads during the review.
4. The run is recorded on the OpalScript **Runs** page, like any other script run.

Context provider scripts have no backing service user and no owner.

## Write a context provider script

Read the request with `context.get_request()`, fetch what you need, and attach it with `actions.add_context(...)`.

```python theme={null}
def main():
    # The subject is who the access is for: the target user if present, else the requester.
    request = context.get_request()
    subject = entity.get_user(request.target_user_id or request.requester_id)

    resp = http.get(
        url = "https://api.example.com/v1/lookup",
        headers = {"Authorization": "Bearer " + secrets.get("api_token")},
        params = {"email": subject.email},
    )
    if not resp.ok:
        return

    payload = resp.json()
    actions.add_context(
        label = "External profile for " + subject.email,
        source = "example",
        data = {"status": payload.get("status"), "risk_score": payload.get("risk_score")},
    )

main()
```

The host must be on the [egress allowlist](/docs/opalscript-utilitymodules#egress-allowlist), and `api_token` must exist as a [secret](/docs/opalscript-utilitymodules#secrets-module).

### context module

<SectionHeader> context.get\_request() </SectionHeader>

Returns the request Paladin is reviewing. It is the same object documented for [request review](/docs/requestreview-getstarted#request-object).

### actions module

<SectionHeader> actions.add\_context(label=, data=, source=) </SectionHeader>

Adds one item of context for Paladin to read. Call it as many times as you need. It returns `None` and does not end the script.

`add_context` accepts keyword arguments only. Passing a positional argument is an error.

<ParamField path="label" type="string" required>
  A short, human-readable title for the item, such as `"Okta authn posture for alice@example.com"`. Must not be empty.
</ParamField>

<ParamField path="data" type="any" required>
  The context itself: a dict, list, string, number, bool, or `None`. It is serialized to JSON, so keep it to the fields that matter for the decision.
</ParamField>

<ParamField path="source" type="string">
  Where the data came from, such as `"okta"` or `"jamf"`. Helps Paladin and reviewers attribute the context.
</ParamField>

Each item is capped at 8,000 bytes of JSON. Larger items are truncated and end with `…[truncated]`, so summarize rather than passing a whole API response through.

<Warning>
  `data` is passed to Paladin as-is. Unlike comments and logs, a [Secret](/docs/opalscript-utilitymodules#secrets-module) placed inside `data` is **not** masked. Never put a secret in `add_context`.
</Warning>

### Available modules

Context provider scripts can use these [utility modules](/docs/opalscript-utilitymodules):

* `access`, `entity`, `requests`, `risk`, and `time` for reading Opal data
* `http` and `secrets` for calling external APIs

`notifications` and `tickets` are not available, because a context provider script only reads.

## Create a context provider script

1. Navigate to **Admin** > **OpalScript** > **Editor** and select **+** above the script list.
2. Choose **Paladin context** from the script type dropdown.
3. Pick a template, or **Blank script** to start from scratch.
4. Enter a name, then select **Use template** (or **Create blank script**).

### Templates

| Template | What it adds | Secret it expects |
| - | - | - |
| **Keycloak account status & MFA** | Whether the requester's Keycloak account is active and MFA-enrolled | `keycloak_client_secret` |
| **Okta account status & MFA** | The requester's Okta account status, whether it is locked or suspended, and how many MFA factors are active | `okta_api_token` |
| **GitHub account activity** | How recently the requester was active on GitHub, as a live-versus-dormant signal | `github_token` |
| **Custom API (starter)** | A skeleton for pulling fields from any HTTP API | `api_token` |

Each template has constants at the top, such as the base URL of your Okta org, that you set before you use it. Create the secret it expects in **Admin** > **OpalScript** > **Advanced** > **Secrets**, and add the host to **Allowed hosts**.

## Add the script to a Paladin agent

1. Open the Paladin agent's configuration.
2. In the connector list, select the **OpalScript** row.
3. Under **Context scripts**, pick the scripts this agent should run.

An agent can have up to **10** context scripts. The same script can be added to more than one agent.

To see which agents use a script, open it in the editor and select **On Review** in the metadata row. The drawer lists every Paladin agent the script is attached to.

## Test a context provider script

Use **Test run** in the editor, just as for a request review script. The Debug panel lists each `add_context` call with its label and source, so you can confirm what Paladin would receive. Nothing is sent to Paladin during a test run.

## Limits

| Limit | Value |
| - | - |
| Execution time per script | 5 seconds |
| Size of one `add_context` item | 8,000 bytes of JSON (truncated beyond that) |
| Context scripts per Paladin agent | 10 |
| HTTP limits | Same as the [`http` module](/docs/opalscript-utilitymodules#http-module) |

Because the timeout is 5 seconds, keep each script to a few fast API calls. A script that times out adds no context at all, even if it called `add_context` before the timeout.

<Tip>
  `actions.pause` and `actions.poll` aren't available in context provider scripts. The script must finish in one pass.
</Tip>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.