> For the complete documentation index, see [llms.txt](https://teamleader-sdk.mcore-services.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://teamleader-sdk.mcore-services.dev/api-reference/projects/materials.md).

# Materials

Manage materials in Teamleader Focus projects.

Materials in the current ("nextgen") project system — `projects-v2/materials.*`.

```php
Teamleader::materials()
```

|                          |                                                                       |
| ------------------------ | --------------------------------------------------------------------- |
| Class                    | `McoreServices\TeamleaderSDK\Resources\Projects\Materials`            |
| Create / update / delete | ✓ / ✓ / ✓                                                             |
| Pagination               | `page_size` / `page_number`; no totals — a short page is the last one |

## Endpoints

* `projects-v2/materials.assign`
* `projects-v2/materials.create`
* `projects-v2/materials.delete`
* `projects-v2/materials.duplicate`
* `projects-v2/materials.info`
* `projects-v2/materials.list`
* `projects-v2/materials.unassign`
* `projects-v2/materials.update`

## Filters

Passed as the first argument to `list()`. Any other key throws.

| Filter | Description             |
| ------ | ----------------------- |
| `ids`  | Array of material UUIDs |

## Accepted values

Public constants on the class. Body fields and enum values outside these lists throw before the request is sent.

| Constant            | Values                                                                                                                                                                                                                                                           |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ASSIGNEE_TYPES`    | `team`, `user`                                                                                                                                                                                                                                                   |
| `BILLING_METHODS`   | `fixed_price`, `unit_price`, `non_billable`, `parent_fixed_price`                                                                                                                                                                                                |
| `COLORS`            | `#00B2B2`, `#008A8C`, `#992600`, `#ED9E00`, `#D157D3`, `#A400B2`, `#0071F2`, `#004DA6`, `#64788F`, `#C0C0C4`, `#82828C`, `#1A1C20`                                                                                                                               |
| `CREATE_FIELDS`     | `project_id`, `title`, `group_id`, `after_id`, `description`, `billing_method`, `quantity`, `quantity_estimated`, `unit_price`, `unit_cost`, `unit_id`, `fixed_price`, `external_budget`, `internal_budget`, `start_date`, `end_date`, `product_id`, `assignees` |
| `CURRENCIES`        | `BAM`, `CAD`, `CHF`, `CLP`, `CNY`, `COP`, `CZK`, `DKK`, `EUR`, `GBP`, `INR`, `ISK`, `JPY`, `MAD`, `MXN`, `NOK`, `PEN`, `PLN`, `RON`, `SEK`, `TRY`, `USD`, `ZAR`                                                                                                  |
| `STATUSES`          | `to_do`, `in_progress`, `on_hold`, `done`                                                                                                                                                                                                                        |
| `TIME_UNITS`        | `hours`, `minutes`, `seconds`                                                                                                                                                                                                                                    |
| `UPDATE_FIELDS`     | `title`, `description`, `status`, `billing_method`, `quantity`, `quantity_estimated`, `unit_price`, `unit_cost`, `unit_id`, `fixed_price`, `external_budget`, `internal_budget`, `start_date`, `end_date`, `product_id`                                          |
| `UPDATE_STRATEGIES` | `none`, `cascade`                                                                                                                                                                                                                                                |

## Methods

### `list()`

```php
list(array $filters = [], array $options = []): array
```

List materials

Before v2.2.9 filter keys other than `ids` were dropped without a word, as were the paging options.

Response fields per item:

* id, project {id, type}, group (nullable) {id, type: nextgenProjectGroup}
* title, description (nullable), status, billing\_method, billing\_status
* quantity, quantity\_estimated (nullable numbers)
* unit\_price, unit\_cost, amount\_billed, external\_budget, external\_budget\_spent, internal\_budget, price, fixed\_price, cost, margin (nullable {amount, currency})
* unit (nullable) {id, type: priceunit} — null if the default unit is used
* margin\_percentage (nullable) — null without "Costs on projects" access
* assignees \[{assignee: {type, id}, assign\_type}]
* start\_date, end\_date (nullable), product (nullable) {id, type: product}
* `$filters` — ids (a UUID or a list of UUIDs)
* `$options` — page\_size, page\_number

**Throws** `InvalidArgumentException` On an unknown filter key or option

### `info()`

```php
info($id, $includes = null): array
```

Get one material (same fields as a list() item)

* `$id` — Material UUID
* `$includes` — materials.info takes no includes; passing any throws

### `create()`

```php
create(array $data): array
```

Create a material

Requires project\_id and title. `after_id` null places the material at the top of its project or group; omitting it places it at the bottom.

Returns HTTP 201 with data.{id, type}

**Throws** `InvalidArgumentException` When a required field is missing, or a field or value is not accepted

### `update()`

```php
update($id, array $data): array
```

Update a material

Every field is optional; null clears a nullable field. Returns HTTP 204.

* `$id` — Material UUID

**Throws** `InvalidArgumentException` When a field or value is not accepted

### `delete()`

```php
delete($id, ...$additionalParams): array
```

Delete a material (new in v2.2.9)

* `$id` — Material UUID
* `$additionalParams` — Not used

### `duplicate()`

```php
duplicate(string $originId): array
```

Duplicate a material (new in v2.2.9)

* `$originId` — UUID of the material to duplicate

### `byIds()`

```php
byIds(array $ids): array
```

Get materials by ID

### `getResponseStructure()`

```php
getResponseStructure(): array
```

Get response structure documentation

### `assign()`

```php
assign(string $id, string $assigneeType, string $assigneeId): array
```

Assign a user or team

* `$id` — UUID of the project, group, task or material
* `$assigneeType` — user or team
* `$assigneeId` — UUID of the user or team

**Throws** `InvalidArgumentException` When the assignee type is not user or team

### `unassign()`

```php
unassign(string $id, string $assigneeType, string $assigneeId): array
```

Unassign a user or team

* `$id` — UUID of the project, group, task or material
* `$assigneeType` — user or team
* `$assigneeId` — UUID of the user or team

**Throws** `InvalidArgumentException` When the assignee type is not user or team

### `assignUser()`

```php
assignUser(string $id, string $userId): array
```

### `assignTeam()`

```php
assignTeam(string $id, string $teamId): array
```

### `unassignUser()`

```php
unassignUser(string $id, string $userId): array
```

### `unassignTeam()`

```php
unassignTeam(string $id, string $teamId): array
```

### `getAvailableAssigneeTypes()`

```php
getAvailableAssigneeTypes(): array
```

## Examples

Create a new material with unit pricing and quantity tracking:

```php
$material = Teamleader::materials()->create([
                "project_id" => "49b403be-a32e-0901-9b1c-25214f9027c6",
                "title" => "WD-40 Multi-Use Product",
                "description" => "Industrial size lubricant",
                "billing_method" => "unit_price",
                "quantity_estimated" => 12,
                "quantity" => 10,
                "unit_price" => [
                    "amount" => 25.50,
                    "currency" => "EUR"
                ]
            ]);
```

Update an existing material:

```php
$material = Teamleader::materials()->update(
                "material-uuid",
                [
                    "title" => "Updated Material Name",
                    "status" => "in_progress",
                    "quantity" => 15,
                    "quantity_estimated" => 20
                ]
            );
```

Get detailed information about a material:

```php
$material = Teamleader::materials()->info("material-uuid");
```

List materials by IDs:

```php
$materials = Teamleader::materials()->list([
                "ids" => ["uuid1", "uuid2"]
            ]);
```

Create a material with an estimate then update with actual quantity:

```php
$result = Teamleader::materials()->create([
                "project_id" => "project-uuid",
                "title" => "Copper pipe (meters)",
                "billing_method" => "unit_price",
                "quantity_estimated" => 25,
                "unit_price" => ["amount" => 8.50, "currency" => "EUR"],
            ]);
            // Later, update with actual usage
            Teamleader::materials()->update($result["data"]["id"], [
                "quantity" => 22,
                "status" => "done",
            ]);
```

Duplicate a material:

```php
$copy = Teamleader::materials()->duplicate("material-uuid");
```

Assign a user to a material:

```php
Teamleader::materials()->assignUser("material-uuid", "user-uuid");
```

Delete a material:

```php
Teamleader::materials()->delete("material-uuid");
```
