> For the complete documentation index, see [llms.txt](https://docs.pandium.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.pandium.com/getting-started/pandium-integration-tutorial/pokemon-of-the-day-part-1/write-the-integration-in-typescript/run-the-integration-locally.md).

# Run the Integration Locally

Run your Pandium TypeScript integration locally with the Pandium CLI, which supplies your tenant's secrets, configs, and context, then trim the starter code down to a clean starting point.

Pandium gives your integration its secrets, tenant settings, and context when it runs on Pandium. During local development `pandium local run` fetches those same values from your tenant and runs your integration with them, so your code behaves the same way in both places.

You can read more about the [environmental variables](/getting-started/anatomy-of-an-integration/environment-variables.md) Pandium gives your integration during run time.

1. Run `pandium local build && pandium local run <tenant-id>`, replacing `<tenant-id>` with the ID you noted when you installed the CLI.
2. The CLI will warn that it may refresh the tenant's secrets and ask you to confirm. Enter `y`.

You should see something like this logged:

```
This may refresh the secrets on tenant my-tenant (1234) of integration pokemon (5678). Are you sure you want to do this? (y/n)
y
******
[2026-09-08 10:15:02:123] [index] INFO: Hello from a Pandium integration, written in TypeScript!
[2026-09-08 10:15:02:124] [index] INFO: This run is in mode: normal
[2026-09-08 10:15:02:124] [index] INFO: Tenant configs: {"boolean_1":"true"}
[2026-09-08 10:15:02:125] [index] INFO: new random number: 482913
[2026-09-08 10:15:02:125] [lib] INFO: updating metadata with {"random_number":482913}
{"random_number":482913}
******
Pandium run status: ✅ Success
```

The starter code just ran in normal mode with your tenant's secrets and configs, and you never had to look up your Slack token! Everything between the two rows of asterisks is your integration's own output, and the CLI reports the run's status at the end. The rest of this tutorial only shows the integration's output when describing what to expect from a run.

A few things are worth noticing about the starter code before you change it:

* src/lib.ts defines a `Pandium` class. `Pandium.fromEnv()` gathers up everything Pandium passed to the run: `pandium.config` holds the tenant's connection settings (the `PAN_CFG_` variables), `pandium.secrets` holds its secrets (`PAN_SEC_`), and context (`PAN_CTX_`) is exposed through methods like `pandium.runMode()`.
* Logs are written with a log4js `logger`, which prints to [stderr](/getting-started/anatomy-of-an-integration/environment-variables/stderr.md). When writing an integration to run on Pandium, [the standard out](/getting-started/anatomy-of-an-integration/environment-variables/stdout.md) is reserved for one thing: the last line printed there is saved by Pandium as the tenant's metadata. That's what `pandium.updateMetadata` is doing with the random number, and you will use it to keep track of the Pokémon of the day later on.
* The `run` function branches on run mode. A Pandium integration can run in `normal`, `init`, or `webhook` mode, but the Academy only needs a normal sync for now.

None of the starter's demo logic (random numbers, dynamic colors, webhooks) is relevant to the Pokémon Trainer Academy, so let's clear it out.

3. In src/index.ts remove the `run` function and simplify `main` so that it only logs the run mode and the tenant configs. Your src/index.ts should now look like this:

```typescript
import * as dotenv from 'dotenv'
dotenv.config({ quiet: true })
import log4js from 'log4js'
import { Pandium } from './lib.js'

const logger = log4js.getLogger('index')

const main = async () => {
    const pandium = Pandium.fromEnv()
    logger.info(`This run is in mode: ${pandium.runMode()}`)
    logger.info(`Tenant configs: ${JSON.stringify(pandium.config)}`)
}

main().then(
    () => {},
    () => {
        process.exitCode = 1
    }
)
```

4. Run `pandium local build && pandium local run <tenant-id>` again. Now only two lines should be logged:

```
[2026-09-08 10:16:40:301] [index] INFO: This run is in mode: normal
[2026-09-08 10:16:40:302] [index] INFO: Tenant configs: {"boolean_1":"true"}
```

{% hint style="info" %}
If you ever need to override a value for a local run, the CLI will read a .env file in your integration folder and let its values take precedence over the ones saved on Pandium. If you do add one, put it in your .gitignore so secrets are never pushed to your remote repository.
{% endhint %}

The configs logged here are the irrelevant options from the starter files' PANDIUM.yaml that you saw when you created your tenant. Let's update the PANDIUM.yaml to remove them.


---

# 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.pandium.com/getting-started/pandium-integration-tutorial/pokemon-of-the-day-part-1/write-the-integration-in-typescript/run-the-integration-locally.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.
