> For the complete documentation index, see [llms.txt](https://docs.thewalletcrew.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.thewalletcrew.io/developers-guides/integration-guides/custom-connectors.md).

# Custom connectors

## Custom connectors

Custom connectors extend The Wallet Crew with tenant-side scripts. They are useful when pass data must be enriched from an external system or when custom logic must run when a pass is installed or uninstalled.

This pattern keeps the integration inside the tenant runtime. It avoids exposing connector logic in client-side code and makes external calls easier to control.

Use this guide when no native connector exists for your software and the integration must be implemented with runtime scripting.

### Build checklist

Before writing scripts, confirm the following:

| Item                | Expected output                                                                |
| ------------------- | ------------------------------------------------------------------------------ |
| Identifier strategy | Stable ID fields available on every pass-relevant record                       |
| Data ownership      | Source systems for profile, loyalty, transaction, and ticket data are explicit |
| Trigger model       | Clear decision on when `Fill`, install, and uninstall hooks are used           |
| Security model      | API keys or OAuth strategy defined for external calls                          |
| Error handling      | Retry, timeout, and monitoring behavior agreed with operations teams           |

If one of these items is missing, align it first to avoid unstable connector behavior in production.

<details>

<summary><strong>Real-world examples</strong></summary>

* A Brand enriches a loyalty pass with points and tier data from an external CRM.
* A partner fills a pass with profile attributes coming from a proprietary API.
* A team synchronizes pass installation status with a marketing or analytics platform.

</details>

### Requirements

To create a script, open **Settings → Advanced → Advanced Configuration**. This section contains the tenant configuration files.

Script files are stored under `/server/script/...`.

{% hint style="info" %}
`customProvider` is only an example name. You can replace every `customProvider` occurrence with the connector name that should be registered or you can keep it.
{% endhint %}

### Runtime constraints

Custom connector scripts run inside a sandboxed, versioned execution environment. The following constraints apply to all scripts regardless of where they are executed.

**Execution time**

Each script invocation has a strict maximum execution time of 5 seconds. Scripts that exceed this limit are terminated. Design scripts to call lightweight, low-latency endpoints and avoid blocking operations.

**Approved libraries only**

The nil.js runtime exposes a restricted set of libraries explicitly approved and maintained by The Wallet Crew. External or third-party libraries cannot be imported. This ensures deterministic behavior, reduces the attack surface, and keeps performance predictable.

**Automated validation**

Scripts undergo automated validation — including linting and schema checks — before they can be saved and executed. A script that fails validation cannot be deployed.

**Execution model**

Depending on the extensibility endpoint, a script may run in one of two modes:

* **Synchronous**: executed inline within an API call, contributing to the response. Latency is critical in this mode.
* **Asynchronous**: executed as part of a background workflow triggered through messaging. More tolerant of processing time but still subject to the 5-second limit per invocation.

Memory usage is not capped by a fixed quota, but abnormal consumption triggers monitoring alerts. If a script causes sustained resource pressure, it will be reviewed.

### Create the script file

In the file explorer, create a file such as `/server/script/customProvider.js`.

If the folder or file does not exist yet, create it first. To create a folder from the file explorer, enter the folder name followed by `/`.

### Register the connector

Once the script file exists, add the connector registration:

```js
export default function(context) {
  context.register('runtime.scriptable.customerProvider.customProvider', {
    Fill: fill
  });
  context.register('runtime.wallet.passUpdater', {
    OnPassInstalled: onPassInstalled,
    OnPassUninstalled: onPassUninstalled
  });
}
```

### Fill function

The `Fill` function is the entry point used to retrieve and inject external data into the pass lifecycle.

In practice, this function receives the current pass context, calls the required external services, and returns or applies the data needed by the connector. The exact implementation depends on the external system being connected.

The runtime contract for `Fill` is available in the [API reference](https://docs.thewalletcrew.io/api-reference).

### Example implementation

This example reads the external identifiers that are added when a pass is created, calls an external API, and maps the response back into the entity.

```javascript
async function fill(entity) {
  const externalId = entity["id.externalId"];
  if (!externalId) {
    return false;
  }

  try {
    const res = await fetch("https://example.com/api/wallet", {
      Method: "GET",
      Headers: {
        "Content-Type": "application/json",
        "X-API-KEY": API_KEY,
      }
    });

    const item = JSON.parse(res.ResponseText)[0];

    entity.eventName = item?.eventName ?? `event ${externalId}`;
    entity.external_last_name = item?.lastName ?? null;
    entity.external_first_name = item?.firstName ?? null;
    entity.external_image_url = item?.imageUrl ?? null;
    entity.external_logo = item?.logo ?? null;
    entity.external_color = item?.color ?? null;
    entity.external_background_color = item?.backgroundColor ?? null;

    return true;
  } catch (e) {
    entity.external_error = `An error occurred while calling the external API: ${e}`;
    return true;
  }
}
```

### What this registration does

The script registers two runtime entry points. Each one serves a different purpose.

`runtime.scriptable.customerProvider.customProvider`

This registration exposes a function named `Fill`. The `Fill` function is used to populate passes with external data.

This is the place to call external APIs and enrich pass data with information coming from partner systems, CRM platforms, loyalty engines, or any other backend connected to the project.

`runtime.wallet.passUpdater`

This registration exposes two lifecycle hooks:

* `OnPassInstalled`
* `OnPassUninstalled`

These hooks are triggered when a pass is installed or uninstalled. They make it possible to run connector logic at the exact moment the wallet lifecycle changes.

Typical uses include synchronizing installation status, starting a welcome flow, or stopping reminder communications once the pass is already installed.

The runtime contract for these hooks is available in the [API reference](https://docs.thewalletcrew.io/api-reference).

The runtime reference above is the source of truth for install and uninstall hook behavior.

### Related patterns

Use connector-specific pages when you only need one behavior:

* [Pass installation and uninstallation hooks](https://github.com/TheWalletCrew/docs/tree/main/connectors/custom-connector/installation-changed-extensibility.md)
* [EmailSender extensibility](https://github.com/TheWalletCrew/docs/tree/main/connectors/custom-connector/emailsender-extensibility.md)

### FAQ

<details>

<summary><strong>Does the script file need to be named <code>customProvider.js</code>?</strong></summary>

No. The filename can follow any naming convention used by the project. What matters is the runtime registration key used inside the script.

</details>

<details>

<summary><strong>Can <code>customProvider</code> be replaced with another connector name?</strong></summary>

Yes. Replace `customProvider` with the connector name that should be exposed by the runtime registration.

</details>

<details>

<summary><strong>When should <code>runtime.wallet.passUpdater</code> be registered?</strong></summary>

Register it when the integration must react to pass installation or uninstallation events. If only pass enrichment is needed, the customer provider registration may be enough.

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.thewalletcrew.io/developers-guides/integration-guides/custom-connectors.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
