# Resource bindings

> Resource bindings in Python functions: how a declared platform resource is remapped to the target tenant at runtime, and why bindings are maintained by hand.

A resource binding is a declared reference to a platform resource that the runtime can remap. When a function reads an Orchestrator asset or calls an Integration Service connection, it names that resource with an identifier fixed at design time. The binding turns that identifier into a declared dependency of the package rather than a hardcoded string, so the same package can run in another tenant against that tenant's own resource.

Bindings are declared in `bindings.json` at the project root, and they are read both during a local run and during a job run in Orchestrator.

## Why a resource identifier is not a function input

A connection id, asset name, or bucket name identifies infrastructure, not data. Passing one through `Input` works mechanically, but it has three consequences:

- Every caller has to know the target tenant's configuration.
- The override mechanism never runs, because the identifier arrives as data rather than as a declared dependency.
- The package no longer declares that it needs the resource, so deployment tooling cannot inventory or remap it.

A binding keeps the identifier out of the invocation contract and inside the package manifest, where deployment tooling can see it.

## How a binding resolves at runtime

Each SDK call that supports overrides resolves its resource in four stages:

1. The platform supplies the mapping configured for the current tenant, one entry per declared binding.
2. The SDK method reads its own resource identifier from the call arguments.
3. When that identifier matches a declared binding with a mapping, the SDK substitutes the mapped value before issuing the request.
4. When no mapping matches, the call proceeds with the design-time identifier.

The fourth stage is a silent fallback, recorded in the run log:

```
No resource overwrite matched for connection key='connection.<design-time-id>' on retrieve
```

A local run has no mappings configured, so this line appears for every binding and is expected there. The same line in a job running in a target tenant means the binding was never mapped in that tenant, and the function is reaching for the design-time resource instead.

:::warning
An unmapped binding does not fail the job. Because the call falls back to the design-time identifier, a function deployed to another tenant can attempt to reach the original tenant's resource.
:::

## The bindings.json file

Each entry in the `resources` array declares one resource. A connection binding looks like this:

```json
{
  "$schema": "https://cloud.uipath.com/draft/2024-12/bindings",
  "version": "2.0",
  "resources": [
    {
      "resource": "connection",
      "key": "00000000-0000-0000-0000-000000000000",
      "value": {
        "ConnectionId": {
          "defaultValue": "00000000-0000-0000-0000-000000000000",
          "isExpression": false,
          "displayName": "Microsoft Outlook 365 Connection"
        },
        "Connector": {
          "defaultValue": "uipath-microsoft-outlook365",
          "isExpression": false,
          "displayName": "Connector"
        }
      },
      "metadata": {
        "Connector": "uipath-microsoft-outlook365",
        "UseConnectionService": "True",
        "BindingsVersion": "2.2"
      }
    }
  ]
}
```

| Field | Purpose |
|---|---|
| `resource` | The resource type: `asset`, `bucket`, `queue`, `process`, `app`, `index`, `connection`, or `mcpServer`. |
| `key` | The binding's identifier, matched against the identifier in the SDK call. |
| `value` | The design-time values the runtime remaps. Connections carry `ConnectionId` and `Connector`; other types carry `name` and `folderPath`. |
| `metadata` | Descriptive fields the platform reads when it resolves and displays the binding. |

The `key` format depends on the resource type. Connections use the connection id on its own. Every other type joins the resource name and folder path with a dot, as in `my_asset.Finance`, and drops the separator when no folder path applies.

The design-time identifier appears in three places that must agree: the literal in your function code, the binding's `key`, and the `defaultValue` of the identifying field inside `value`. A mismatch between them means the override lookup finds nothing and the fallback applies.

## SDK calls that participate in resource overrides

Only the calls below are remapped. An identifier passed to any other method is used exactly as written.

| SDK call | Resource type | Identifier |
|---|---|---|
| `assets.retrieve`, `assets.retrieve_credential` | `asset` | First positional argument, joined with `folder_path` |
| `buckets.*` (all methods) | `bucket` | `name`, joined with `folder_path` |
| `queues.create_item`, `create_items`, `create_transaction_item` | `queue` | Queue name, joined with `folder_path` |
| `processes.invoke`, `jobs.resume` | `process` | `name` or `process_name`, joined with `folder_path` |
| `tasks.create`, `tasks.retrieve` | `app` | `app_name`, joined with `app_folder_path` |
| `context_grounding.*` (all methods) | `index` | `name`, joined with `folder_path` |
| `connections.retrieve` | `connection` | First positional argument, used on its own |
| `mcp.retrieve` | `mcpServer` | `slug`, joined with `folder_path` |

The synchronous and asynchronous variants of each method behave identically. Calls to `llm`, `documents`, `entities`, `guardrails`, `attachments`, and `folders` produce no bindings, and neither does `assets.update`.

## Why bindings are not derived from your code

`uipath init` creates `bindings.json` with the required structure when the file is absent, and leaves an existing file untouched. It does not read resource calls out of your code, so re-running it after a change to `Input`, `Output`, or a resource call does not update bindings. The file is maintained by hand.

Resource names are not always knowable before the function runs. A literal such as `sdk.assets.retrieve("SMTP_HOST")` is detectable by static analysis, but `sdk.assets.retrieve(input.asset_name)` or a name read from an environment variable has no value to bind at analysis time.

Because the tooling cannot distinguish an unresolvable name from a project that intentionally declares nothing, partial inference would silently discard hand-written entries, and the loss would surface only as a deployment failure in the target tenant. Leaving the file untouched is the safer behavior.

:::tip
The UiPath skills for coding agents include a bindings reference that a coding agent can follow to keep `bindings.json` in step with your code. See [github.com/UiPath/skills](https://github.com/UiPath/skills).
:::

## Moving a function between tenants

Bindings are what make a single package deployable to more than one tenant. When a function project belongs to a solution, the solution aggregates the bindings declared by each of its projects into a resource list, and deployment maps every entry in that list to a resource in the target tenant.

Two commands establish that link:

- [`uip solution project add`](https://docs.uipath.com/uipath-cli/standalone/latest/user-guide/uip-solution-project) registers the function project in a solution.
- [`uip solution resource refresh`](https://docs.uipath.com/uipath-cli/standalone/latest/user-guide/uip-solution-resource) re-scans the projects and syncs their declared bindings into the solution's resource list.

The result is one `.nupkg` that runs unchanged in development, test, and every customer tenant, because the identifiers it carries are remapped rather than fixed.

## Next steps

- [Declare a resource binding](declaring-resource-bindings.md) — add a binding and verify that it resolves.
- [Accessing platform services](accessing-platform-services.md) — the SDK calls that bindings apply to.
