[Canonical page](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-level/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# JsonProject.level

Resolves resource overallocation in a [`JsonProject`](<https://docs.dhtmlx.com/planning-engine/wasm/api/json-project.md>). It solves the project first, then writes persistent `leveling_delay` and `pauses` edits to tasks and accepts the leveled state as the baseline.

The project doesn't need to be solved beforehand. To see leveling without committing it, call `level` on a [`clone`](<https://docs.dhtmlx.com/planning-engine/wasm/api/json-project.md#clone>).

## Signature

```ts
level(options_json: string): string
```

Synchronous. Changes the project's state.

## Request

| Argument | Payload | Required | Description |
|---|---|---|---|
| `options_json` | [`LevelingOptions`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/leveling-options.md>) | yes | Scope, order, basis and how leveling may move tasks. `"{}"` for defaults |

## Behavior

- The initial solve uses default solve options and works on the current state, including edits from [`apply`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-apply.md>). When the project is already solved with default options, the cached result is reused.
- Forward scheduling only. A project with `config.schedule_from_end: true` is rejected.
- On a cyclic project, `level` returns the initial solve's result with `rounds: 0` and edits no task.
- The leveled dates, delays and pauses replace the baseline, and `baselineId` in the result names the new baseline.
- Tasks touched by successful `apply` calls since the last solve appear in `updates` as direct updates.

## Response

A success [envelope](<https://docs.dhtmlx.com/planning-engine/wasm/wire-format.md#envelope>) with a [`LevelingResult`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-level.md#levelingresult>).

### LevelingResult

The fields of a [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/schedule-result.md>), flattened into one object, plus `rounds`. `analytics` and `profile` aren't present in a `level` result.

| Field | Type | Description |
|---|---|---|
| `rounds` | `number` | Number of leveling rounds that proposed a correction. `0` on a cyclic project |
| `baselineId` | `string` | The accepted baseline after leveling |
| `updates` | [`Mutation`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/schedule-result.md#mutation>)`[]` | Tasks that changed compared to the state the call started from: dates plus `leveling_delay` and `pauses` (snake_case). Empty on a cyclic project. See [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/schedule-result.md>) |
| `cycles` | [`Cycle`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/schedule-result.md#cycle>)`[]` | Cycles found by the initial solve |
| `conflicts` | [`Conflict`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/schedule-result.md#conflict>)`[]` | Conflicts from the final scheduling pass |
| `notes` | [`Note`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/schedule-result.md#note>)`[]` | Notes from the final scheduling pass |
| `converged` | `boolean` | Whether the final scheduling pass reached a fixed point within `max_iterations` |
| `overallocations` | [`Overallocation`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/overallocation.md>)`[]` | Overallocation left after leveling, each window with a `reason`. Present only when the project has assignments |

Each `updates` entry can be passed back to [`apply`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-apply.md>) as a task update.

## Errors

| `kind` | `code` | When |
|---|---|---|
| `parse` | `null` | `options_json` isn't valid JSON, has a field of the wrong type (for example `allowSplits: "no"`), or has an unknown field (for example `maxRounds`) |
| `validation` | `null` | An option has a value it doesn't accept (for example `step: "jump"`, `path: null`); an id in `scope.resources` or `scope.tasks` doesn't exist (`path` `scope.resources` / `scope.tasks`); the project has `config.schedule_from_end: true` (`path` `config.schedule_from_end`) |
| `project` | `needs-resync` | An earlier `apply` rejected an operation, and no `solve` with data has followed |

Calling `level` on a freed handle throws at host level; see [Wire format](<https://docs.dhtmlx.com/planning-engine/wasm/wire-format.md#host-level-failures>).

## Example

Level two tasks that share one resource, first on a copy, then for real, in Node:

```js
const { JsonProject } = require("./pkg-node/plen_wasm.js");

const data = {
  config: { use_calendars: false },
  tasks: [
    { id: "a", start_date: "2026-08-10 00:00", duration: 2 },
    { id: "b", start_date: "2026-08-10 00:00", duration: 2 },
  ],
  resources: [{ id: "dev" }],
  assignments: [
    { task: "a", resource: "dev", value: 1 },
    { task: "b", resource: "dev", value: 1 },
  ],
};

const project = new JsonProject("{}");
try {
  const loaded = JSON.parse(project.solve(JSON.stringify(data), "{}"));
  if (!loaded.ok) throw new Error(loaded.error.message);

  // A proposal: level a copy, the project keeps its dates.
  const draft = project.clone();
  try {
    const proposal = JSON.parse(draft.level("{}"));
    if (proposal.ok) {
      for (const u of proposal.result.updates) {
        console.log(u.id, u.start_date, u.leveling_delay);
      }
    }
  } finally {
    draft.free();
  }

  // Commit: level the project itself.
  const envelope = JSON.parse(project.level("{}"));
  if (envelope.ok) {
    console.log(`${envelope.result.rounds} round(s)`);
  } else {
    console.error(envelope.error.kind, envelope.error.code, envelope.error.message);
  }
} finally {
  project.free();
}
```

Request (`options_json`):

```json
{}
```

Response:

```json
{
  "ok": true,
  "result": {
    "baselineId": "<opaque token>",
    "conflicts": [],
    "converged": true,
    "cycles": [],
    "notes": [],
    "overallocations": [],
    "rounds": 2,
    "updates": [
      {
        "id": "b",
        "kind": "task",
        "start_date": "2026-08-12 00:00",
        "end_date": "2026-08-14 00:00",
        "leveling_delay": 2
      }
    ]
  }
}
```

An unknown option fails the whole call:

```json
{
  "ok": false,
  "error": {
    "kind": "parse",
    "code": null,
    "message": "unknown field `maxRounds`, expected one of `scope`, `basis`, `order`, `withinSlackOnly`, `clearFirst`, `allowSplits`, `step`",
    "path": null
  }
}
```

## Related

- [`LevelingOptions`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/leveling-options.md>) - scope, order and how tasks may move
- [`Overallocation`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/overallocation.md>) - the overallocation left after leveling
- [`JsonProject.evaluate`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-evaluate.md>) - score leveled candidates without changing the project
- [Leveling Resources](<https://docs.dhtmlx.com/planning-engine/wasm/resources-and-assignment/leveling-resources.md>) - how leveling picks and moves tasks
- [Preview leveling](<https://docs.dhtmlx.com/planning-engine/wasm/how-to/preview-leveling.md>) - level a copy before committing
