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


# JsonProject.slots

Turns a [`Pool`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-slots.md#pool>) of workers with skills and a list of [`Requirement`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-slots.md#requirement>)s into the [`Slot`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/slot.md>)s that [`suggestAssignments`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-suggest-assignments.md>) takes. Existing active assignments are bound to the slots, so repeated runs stay stable after a commit. Needs a solved [`JsonProject`](<https://docs.dhtmlx.com/planning-engine/wasm/api/json-project.md>) with no unsolved edits.

## Signature

```ts
slots(pool_json: string, requirements_json: string): string
```

Synchronous and read-only: reads the project's accepted state and never modifies the handle. In the browser build, call it only after [`init()`](<https://docs.dhtmlx.com/planning-engine/wasm/api/init.md>) has resolved.

## Request

| Argument | Payload | Required | Description |
|---|---|---|---|
| `pool_json` | [`Pool`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-slots.md#pool>) | yes | Workers that may be assigned. Pool order sets the order of each slot's `allowed`. |
| `requirements_json` | [`Requirement`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-slots.md#requirement>)`[]` | yes | What each task needs. `[]` returns an empty array. |

### Pool

The workers to choose from, in order of preference.

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `workers` | [`Worker`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-slots.md#worker>)`[]` | yes | | Candidate workers. Their order is the order of `allowed` in every slot. |

### Worker

One resource of the project and its skills.

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `resource` | [`Id`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/common.md#id>) | yes | | A resource of the project. Each resource can appear as one worker only. |
| `skills` | [`Skill`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-slots.md#skill>)`[]` | no | `[]` | The worker's skills. `null` = `[]`. |

### Skill

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `id` | [`Id`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/common.md#id>) | yes | | Skill name. Listed at most once per worker. |
| `value` | `number` | no | `1` | Skill grade, e.g. `0`/`1` or an integer level. Must be finite. `null` = `1`. |

### Requirement

What one task needs: `count` workers that satisfy every skill match.

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `id` | [`Id`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/common.md#id>) | yes | | Unique among the requirements. Slot ids are built from it. |
| `task` | [`Id`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/common.md#id>) | yes | | A scheduled task of the project. |
| `matches` | [`SkillMatch`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-slots.md#skillmatch>)`[]` | yes | | Conditions a worker must meet, all of them. `[]` allows every pool worker. |
| `count` | `number` | no | `1` | Number of slots, a whole number `>= 1`. `null` = `1`. |
| `units` | `number` | no | `1` | Value of a new assignment created for a slot (`1` = 100 %). Finite and `> 0`. `null` = `1`. |
| `assignments` | [`Id`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/common.md#id>)`[] \| null` | no | `null` | Explicit binding of existing assignments to this requirement's slots, replacing automatic binding. `null` or absent = automatic binding; `[]` = all slots empty. At most `count` entries. |

The slot ids of a requirement become assignment ids on its task once a proposal is committed. Keep each requirement id on the same task across runs: reusing it for another task makes the call fail at `requirements[i].id`.

### SkillMatch

One condition on a worker's skill grade. A skill the worker doesn't list counts as `0`.

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `skill` | [`Id`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/common.md#id>) | yes | | Skill to check. |
| `op` | `string` | no | `"at_least"` | `"at_least"` (grade `>=` value), `"no_more"` (grade `<=` value), `"equal"` (grade `==` value). `null` = `"at_least"`. |
| `value` | `number` | no | `1` | Grade to compare with. Must be finite. `null` = `1`. |

`{ "skill": "backend" }` means "has the skill".

All five shapes reject unknown fields.

## Response

The success envelope carries a [`Slot`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/slot.md>)`[]`: for each requirement, `count` slots, in requirement order, then instance order. The envelope shape is described in [Wire format](<https://docs.dhtmlx.com/planning-engine/wasm/wire-format.md#envelope>).

| Slot field | Value |
|---|---|
| `id` | `"<requirement id>#<n>"`, `n` = 1..`count` (for example `be#1`, `be#2`) |
| `task` | The requirement's `task` |
| `allowed` | Pool workers for which every match of the requirement holds, in pool order |
| `value` | The requirement's `units` |
| `assignment` | The existing active assignment bound to this slot; omitted when the slot is empty |

The array can be passed to [`suggestAssignments`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-suggest-assignments.md>) as is (`JSON.stringify(envelope.result)`).

## Behavior

The task's existing active assignments are bound per task, across all requirements on that task, in three steps:

1. **Slot id.** An assignment whose id is one of the requirement's slot ids binds to that slot, even when its resource isn't in `allowed`. The slot is then broken (`"broken"` in [`SuggestOptions.targets`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-suggest-assignments.md#suggestoptions>)). This step runs for every requirement of the task before any explicit list.
2. **Explicit list.** A non-null `assignments` replaces automatic binding for that requirement. A listed slot-id assignment keeps its slot, the other entries take the remaining slots in order, and leftover slots stay empty.
3. **Matching.** A deterministic maximum bipartite matching binds the remaining assignments to the empty slots of requirements without a list. An assignment can take a slot only when its resource is in the slot's `allowed`. Input order decides between equal matchings, and an earlier requirement can't take the only worker a later one fits.

Assignments left unbound stay on the task without a slot.

## Errors

The first failing check wins: decoding of both arguments, then the project state, then the pool, then each requirement in input order.

| Error | When |
|---|---|
| `kind: "parse"` | Malformed JSON, wrong JSON type (e.g. an array for `pool_json`), a missing required field (`workers`, `resource`, `id`, `task`, `matches`, `skill`), or an unknown field. `path: null`. |
| `kind: "validation"`, `path: null` | A value the decoder rejects: an unknown `op` string, or a `count` that isn't a nonnegative whole number. |
| `kind: "project"`, `code: "no-baseline"` | The project was never solved, or has unsolved edits after [`apply`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-apply.md>). |
| `kind: "validation"` with a path | The pool or a requirement doesn't fit the project. See the table below. |

Validation paths:

| Path | When |
|---|---|
| `pool.workers[i].resource` | Unknown resource, or the same resource listed as two workers |
| `pool.workers[i].skills[j].id` | The same skill listed twice on one worker |
| `pool.workers[i].skills[j].value` | Not finite |
| `requirements[i].id` | Duplicate requirement id, or one of its slot ids is an active assignment on another task |
| `requirements[i].task` | Unknown or unscheduled task |
| `requirements[i].count` | `0` |
| `requirements[i].units` | Not finite or `<= 0` |
| `requirements[i].matches[j].value` | Not finite |
| `requirements[i].assignments` | More entries than `count`, or an assignment with one of this requirement's slot ids left out of the list |
| `requirements[i].assignments[k]` | Unknown or inactive assignment, an assignment on another task, or one bound twice (the message names the other requirement when it already holds it, e.g. `already bound to requirement "fe"`) |

There's no `stale-baseline`: the call takes no `baselineId`.

Errors come back in the returned envelope; the call doesn't throw for bad input. Calling it on a freed handle throws at host level. See [Error handling](<https://docs.dhtmlx.com/planning-engine/wasm/error-handling.md>).

## Example

The project has tasks `B` and `C`, resources `ann`, `bob` and `cy`, and an assignment `a1` of `ann` on `B`.

Request (`pool_json`):

```json
{
  "workers": [
    { "resource": "ann", "skills": [{ "id": "backend" }] },
    { "resource": "bob", "skills": [{ "id": "backend", "value": 2 }, { "id": "qa", "value": 2 }] },
    { "resource": "cy", "skills": [{ "id": "qa" }] }
  ]
}
```

Request (`requirements_json`):

```json
[
  { "id": "be", "task": "B", "matches": [{ "skill": "backend" }], "count": 2 },
  { "id": "qa", "task": "C", "matches": [{ "skill": "qa", "value": 2 }], "units": 0.5 }
]
```

Response:

```json
{
  "ok": true,
  "result": [
    { "allowed": ["ann", "bob"], "assignment": "a1", "id": "be#1", "task": "B", "value": 1 },
    { "allowed": ["ann", "bob"], "id": "be#2", "task": "B", "value": 1 },
    { "allowed": ["bob"], "id": "qa#1", "task": "C", "value": 0.5 }
  ]
}
```

`a1` is bound to `be#1` by matching, because `ann` is in its `allowed`. `cy` has `qa` at grade 1 only, so `qa#1` allows `bob` alone.

Host call in Node, passing the slots on to `suggestAssignments`:

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

const data = {
  tasks: [
    { id: "B", start_date: "2026-08-10 08:00", duration: 2 },
    { id: "C", start_date: "2026-08-10 08:00", duration: 1 },
  ],
  resources: [{ id: "ann" }, { id: "bob" }, { id: "cy" }],
  assignments: [{ id: "a1", task: "B", resource: "ann", value: 1 }],
};
const pool = {
  workers: [
    { resource: "ann", skills: [{ id: "backend" }] },
    { resource: "bob", skills: [{ id: "backend", value: 2 }, { id: "qa", value: 2 }] },
    { resource: "cy", skills: [{ id: "qa" }] },
  ],
};
const requirements = [
  { id: "be", task: "B", matches: [{ skill: "backend" }], count: 2 },
  { id: "qa", task: "C", matches: [{ skill: "qa", value: 2 }], units: 0.5 },
];

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

  const envelope = JSON.parse(
    project.slots(JSON.stringify(pool), JSON.stringify(requirements)),
  );
  if (!envelope.ok) {
    const { kind, code, path, message } = envelope.error;
    throw new Error(`${kind} ${code ?? path ?? ""}: ${message}`);
  }

  const proposal = JSON.parse(
    project.suggestAssignments(JSON.stringify(envelope.result), "{}"),
  );
  console.log(proposal.result.changes);
} finally {
  project.free();
}
```

## Related

- [`Slot`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/slot.md>) - the result shape
- [`JsonProject.suggestAssignments`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-suggest-assignments.md>) - fills, repairs or rebalances the slots
- [Skills and requirements](<https://docs.dhtmlx.com/planning-engine/wasm/resources-and-assignment/skills-and-requirements.md>) - workers, skills and requirements end to end
- [Suggesting assignments](<https://docs.dhtmlx.com/planning-engine/wasm/resources-and-assignment/suggesting-assignments.md>) - what happens to the slots next
