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

# Connecting your app

> Keys, environments, the WebSocket and webhook modes, and how definitions reach each environment.

## The client

```ts theme={"theme":{"light":"github-light","dark":"vitesse-dark"}}
import { createPult } from "pult"
import project from "../.pult"

export const pult = createPult({ project })
```

`createPult` reads `PULT_KEY` from the environment. You can pass `key` directly instead, `url` if you run Pult somewhere other than `https://app.pult.sh` (`PULT_URL` works too), and `fetch` to send Pult's requests some other way, like through a proxy.

The client gives you:

* `pult.send(inbox, item)` to send an item. See [Sending items](/build/sending).
* `pult.listen()` to receive requests over a WebSocket.
* `pult.handler()` to receive requests over HTTP instead.
* `pult.log(resource, id, text)` to add to a record's history. See [Hooks and history](/build/hooks).
* `pult.receive(message)` to thread an email reply onto its item. See [Email](/build/email).
* `pult.flag(flag, key)` to read a flag. See [Flags](/build/flags).
* `pult.deploy()` to push your definitions to the environment behind the key.

## Keys

Each environment has its own key, created and rotated in the project's Environments settings. A key looks like `pult_<environment>_<secret>`; the first part says which environment it belongs to, so one variable is all your app needs.

Every request in both directions is signed with the key and carries a timestamp and a one-time nonce. Pult refuses anything unsigned, altered, older than five minutes or seen before, and the SDK does the same for requests from Pult.

## WebSocket or webhook

By default your app opens a WebSocket to Pult with `pult.listen()` and keeps it open. Pult sends action requests, lookups and renders down it, and the SDK answers. This works anywhere a process stays up and needs nothing public.

On serverless platforms, switch to webhooks:

```ts theme={"theme":{"light":"github-light","dark":"vitesse-dark"}}
// pult.config.ts
export default defineConfig({
  project: "acme",
  include: ["src/**/*.pult.ts"],
  delivery: { mode: "webhook", path: "/api/pult" },
})
```

Mount the handler on that route and enter your app's origin for each environment in the project's Environments settings:

```ts theme={"theme":{"light":"github-light","dark":"vitesse-dark"}}
// Cloudflare Workers
import { env } from "cloudflare:workers"

const pult = createPult({ key: env.PULT_KEY, project })
const handle = pult.handler()

export default {
  fetch(request: Request, _: Env, ctx: ExecutionContext) {
    const url = new URL(request.url)
    if (url.pathname === "/api/pult") return handle(request, ctx)
    return app.fetch(request, env, ctx)
  },
}
```

With the `nodejs_compat` flag on, Workers expose your secrets as `process.env` and `createPult({ project })` finds `PULT_KEY` by itself.

Definition files can import `cloudflare:workers`, `cloudflare:email` and `cloudflare:sockets` at the top. `pult build` runs outside Workers, so it swaps them for stand-ins while it reads your definitions. A file can still set up a client with `env.DB` at the top, as long as nothing calls it while the file loads: do the actual work inside your functions.

Pass your platform's context as the second argument so [background actions](/build/actions#background-actions) can keep running after the response. On serverless platforms, `await` every `pult.send` or hand it to `ctx.waitUntil`, so the platform doesn't stop your code before the item is sent.

If your app can't be reached, Pult queues what it needed to deliver and retries with backoff. Actions and replies queued this way show as waiting in the timeline until your app answers.

## Environments

Environments are listed in your config from development to production:

```ts theme={"theme":{"light":"github-light","dark":"vitesse-dark"}}
export default defineConfig({
  project: "acme",
  include: ["src/**/*.pult.ts"],
  environments: ["dev", "staging", "prod"],
  sync: "dev",
})
```

The `sync` environment (the first one unless you say otherwise) follows your code. Whenever your app connects, or sends an item built from definitions Pult hasn't seen, the SDK deploys your current definitions there. Pult refuses that automatic deploy for every other environment: those change only through `pult deploy`.

The console marks the last environment as production. Data never crosses environments; each one has its own items, history and key.

## Deploying definitions

```bash theme={"theme":{"light":"github-light","dark":"vitesse-dark"}}
PULT_KEY=pult_... bunx pult deploy
```

`pult deploy` builds, shows what changes (including who gains or loses which permission) and pushes the manifest. Removing an inbox from your code archives it: its items stay readable under "Removed from code" in the console. See [CLI](/reference/cli).


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