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

# Projects

> A project is the deployable unit in Terse: a TypeScript codebase with one or more jobs that ships as a single bundle.

A project bundles your job definitions, their generated SDK, and a small config file that links them to the Terse platform. You scaffold one with `terse init`, write jobs inside it, and run `terse deploy` to ship the whole project at once.

## On disk

```text theme={null}
my-project/
  src/
    terse.jobs.ts        # job manifest (entry point): one side-effect import per job
    jobs/
      sample-job.ts      # each job lives in its own file here
    terse.generated.ts   # typed SDK (regenerated by `terse generate`)
  terse.config.json      # links the directory to a Terse project
  package.json
  tsconfig.json
  .env.example
```

The config file is the only thing the platform needs to recognize the directory:

<CodeGroup>
  ```json terse.config.json theme={null}
  {
      "projectId": "cmpn2w26d001ibpmx8kr00yc7",
      "name": "deal-automation"
  }
  ```
</CodeGroup>

`projectId` is assigned the first time the project is created and ties this code to a specific project in the Terse app. Commit `terse.config.json` to your repo so anyone cloning it can run `terse deploy` without re-creating the project.

## Lifecycle

1. **Create.** Run `terse init <name>` to scaffold a new project. The CLI sets up the files, installs dependencies, opens the browser to authenticate you, and runs `terse generate` against your connected integrations.
2. **Develop.** Define each job in its own file under `src/jobs/` and import it for side effects from the `src/terse.jobs.ts` manifest. Run `terse generate` again whenever you add or reconfigure an integration.
3. **Test.** Run `terse test` to execute jobs locally against sample events.
4. **Deploy.** Run `terse deploy` to ship the project. New jobs are created, existing jobs are updated, and removed jobs are cleaned up in a single pass.
5. **Monitor.** Open the project in the Terse app to see runs, actions, and failures.

To register an existing codebase as a project without scaffolding, run `terse attach` in its root.

## Where the project runs

Terse splits into a **control plane** (orchestrator, triggers, dashboard, history) and a **data plane** (the runtime that executes your `onTrigger` code). Each can run on Terse Cloud or in your own infrastructure. A project's `terse.config.json` controls only the data plane choice; the control plane choice is set by `TERSE_BACKEND_URL` at the CLI/SDK level.

By default, `terse deploy` zips your code and the Terse Cloud data plane runs it serverlessly. No infrastructure to manage.

To route execution to a data plane you operate, add two fields to `terse.config.json`:

<CodeGroup>
  ```json terse.config.json theme={null}
  {
      "projectId": "cmpn2w26d001ibpmx8kr00yc7",
      "name": "deal-automation",
      "selfHosted": true,
      "remoteServerUrl": "https://terse.your-company.com"
  }
  ```
</CodeGroup>

In this mode, the control plane calls your server when triggers fire instead of running the handler itself. See [Self-hosting the data plane](/self-hosting) for the full setup, or [Self-hosting the control plane](/self-hosting-control-plane) to run the orchestrator inside your network too.

## Where to go next

<CardGroup cols={2}>
  <Card title="Jobs" icon="boxes" href="/core-concepts/jobs">
    The trigger-plus-handler unit you write inside a project.
  </Card>

  <Card title="CLI reference" icon="terminal" href="/reference/cli">
    Every `terse` command and its flags.
  </Card>
</CardGroup>
