# Deleting and restoring

What a DELETE does to each resource, how to read and restore archived records, what can block a delete and who may delete.

A `DELETE` does one of three things, depending on the resource. Check which before you delete anything you may want back.

In the examples, `<your-url>` is the address you sign in to plnnrly on, such as `acme.plnnrly.com`, or your organisation's own domain.

## Three behaviours

- **Archived, can be restored.** The record stays, with a `deleted_at` timestamp, and drops out of lists and reads. `POST …/actions/restore` brings it back.
- **Archived, cannot be restored.** The record stays, with a `deleted_at` timestamp, but it has no restore action. These are lines inside another record, such as the rows of a sales invoice. To get one back, create it again.
- **Deleted permanently.** The record is gone, and the API has no way to bring it back.

Which resource behaves how, taken from the reference:

- **Archived, can be restored:** [`/customer-subscriptions`](https://developers.plnnrly.com/en/docs/api/v2/customer-subscriptions/delete/) ([POST Restore](https://developers.plnnrly.com/en/docs/api/v2/customer-subscriptions/restore/)), [`/customers`](https://developers.plnnrly.com/en/docs/api/v2/customers/delete/) ([POST Restore](https://developers.plnnrly.com/en/docs/api/v2/customers/restore/)), [`/drive-items`](https://developers.plnnrly.com/en/docs/api/v2/drive-items/delete/) ([POST Restore](https://developers.plnnrly.com/en/docs/api/v2/drive-items/restore/)), [`/events`](https://developers.plnnrly.com/en/docs/api/v2/events/delete/) ([POST Restore](https://developers.plnnrly.com/en/docs/api/v2/events/restore/)), [`/projects`](https://developers.plnnrly.com/en/docs/api/v2/projects/delete/) (accepts `force=true`; [POST Restore](https://developers.plnnrly.com/en/docs/api/v2/projects/restore/)), [`/sales-invoices`](https://developers.plnnrly.com/en/docs/api/v2/sales-invoices/delete/) ([POST Restore](https://developers.plnnrly.com/en/docs/api/v2/sales-invoices/restore/)), [`/subscription-products`](https://developers.plnnrly.com/en/docs/api/v2/subscription-products/delete/) ([POST Restore](https://developers.plnnrly.com/en/docs/api/v2/subscription-products/restore/)), [`/subscription-types`](https://developers.plnnrly.com/en/docs/api/v2/subscription-types/delete/) ([POST Restore](https://developers.plnnrly.com/en/docs/api/v2/subscription-types/restore/))
- **Archived, cannot be restored:** [`/sales-invoice-rows`](https://developers.plnnrly.com/en/docs/api/v2/sales-invoice-rows/delete/), [`/subscription-items`](https://developers.plnnrly.com/en/docs/api/v2/subscription-items/delete/), [`/subscription-type-items`](https://developers.plnnrly.com/en/docs/api/v2/subscription-type-items/delete/)
- **Deleted permanently:** [`/absences`](https://developers.plnnrly.com/en/docs/api/v2/absences/delete/), [`/budget-groups`](https://developers.plnnrly.com/en/docs/api/v2/budget-groups/delete/), [`/budgets`](https://developers.plnnrly.com/en/docs/api/v2/budgets/delete/), [`/checkins`](https://developers.plnnrly.com/en/docs/api/v2/checkins/delete/), [`/comments`](https://developers.plnnrly.com/en/docs/api/v2/comments/delete/), [`/company-branches`](https://developers.plnnrly.com/en/docs/api/v2/company-branches/delete/), [`/contact-roles`](https://developers.plnnrly.com/en/docs/api/v2/contact-roles/delete/), [`/contacts`](https://developers.plnnrly.com/en/docs/api/v2/contacts/delete/), [`/customer-categories`](https://developers.plnnrly.com/en/docs/api/v2/customer-categories/delete/), [`/drive-documents`](https://developers.plnnrly.com/en/docs/api/v2/drive-documents/delete/), [`/drive-folders`](https://developers.plnnrly.com/en/docs/api/v2/drive-folders/delete/), [`/free-days`](https://developers.plnnrly.com/en/docs/api/v2/free-days/delete/), [`/furlough-balance-mutations`](https://developers.plnnrly.com/en/docs/api/v2/furlough-balance-mutations/delete/), [`/holidays`](https://developers.plnnrly.com/en/docs/api/v2/holidays/delete/), [`/labels`](https://developers.plnnrly.com/en/docs/api/v2/labels/delete/), [`/notes`](https://developers.plnnrly.com/en/docs/api/v2/notes/delete/), [`/project-links`](https://developers.plnnrly.com/en/docs/api/v2/project-links/delete/), [`/sprints`](https://developers.plnnrly.com/en/docs/api/v2/sprints/delete/), [`/subtasks`](https://developers.plnnrly.com/en/docs/api/v2/subtasks/delete/), [`/tasks`](https://developers.plnnrly.com/en/docs/api/v2/tasks/delete/), [`/timers`](https://developers.plnnrly.com/en/docs/api/v2/timers/delete/), [`/todos`](https://developers.plnnrly.com/en/docs/api/v2/todos/delete/), [`/uploads`](https://developers.plnnrly.com/en/docs/api/v2/uploads/delete/), [`/work-item-links`](https://developers.plnnrly.com/en/docs/api/v2/work-item-links/delete/), [`/work-items`](https://developers.plnnrly.com/en/docs/api/v2/work-items/delete/), [`/work-types`](https://developers.plnnrly.com/en/docs/api/v2/work-types/delete/), [`/workflow-statuses`](https://developers.plnnrly.com/en/docs/api/v2/workflow-statuses/delete/), [`/workflow-transitions`](https://developers.plnnrly.com/en/docs/api/v2/workflow-transitions/delete/), [`/workflows`](https://developers.plnnrly.com/en/docs/api/v2/workflows/delete/), [`/worklogs`](https://developers.plnnrly.com/en/docs/api/v2/worklogs/delete/)

Each `DELETE` operation page says what else happens: what is removed with the record, what is detached and what is kept. Read it before you delete in bulk.

## Read archived records

Archived records are hidden until you ask for them with `trashed`:

| `trashed`               | On a list                  | On a single read                       |
| ----------------------- | -------------------------- | -------------------------------------- |
| `without` (the default) | Live records only.         | An archived record is `404`.           |
| `with`                  | Live and archived records. | Live or archived.                      |
| `only`                  | Archived records only.     | Archived only. A live record is `404`. |

- To edit an archived record, send `trashed=with` with the `PATCH`.
- A list inside an archived record, such as the tasks of an archived project, needs `trashed=with` as well.
- `trashed` works only where a record can be archived. Anywhere else it is `400` [unsupported-parameter](https://developers.plnnrly.com/en/docs/api/v2/guides/errors/#unsupported-parameter).

## Restore

```bash
curl -X POST "https://<your-url>/api/v2/projects/12/actions/restore" \
  -H "Authorization: Bearer <token>" \
  -H "Accept: application/json"
```

- The answer is `200` with the record in the `detail` view. A restore takes no `trashed`: the action finds the archived record itself.
- Restoring needs a token with **Write** for the resource, plus the permissions the operation page names.

Restoring a record that is not archived changes nothing, so a retry is safe. It answers `200`, and the `meta` member of the response carries a warning:

```json
{
  "warnings": [
    {
      "code": "already_in_target_state",
      "detail": "This project was not deleted."
    }
  ]
}
```

A restore is not always a full undo, so read the restore operation before you rely on it. Two examples:

- Deleting a customer also archives its projects. [Restoring the customer](https://developers.plnnrly.com/en/docs/api/v2/customers/restore/) does not bring them back: restore each project.
- [Deleting a sales invoice](https://developers.plnnrly.com/en/docs/api/v2/sales-invoices/delete/) releases the hours that were invoiced on it. [Restoring it](https://developers.plnnrly.com/en/docs/api/v2/sales-invoices/restore/) brings back the invoice and its rows, but not the link to those hours, so they can be invoiced a second time.

## Delete a project

[DELETE /projects/{project}](https://developers.plnnrly.com/en/docs/api/v2/projects/delete/) archives the project. Its notes, Drive items and tags stay in place, so a restore brings it back intact.

`force=true` deletes the project permanently. Its notes are deleted, its Drive items are archived and its tags are detached. A project that is already archived can be deleted permanently too.

```bash
curl -X DELETE "https://<your-url>/api/v2/projects/12?force=true" \
  -H "Authorization: Bearer <token>" \
  -H 'If-Match: "projects-12-t1791995400-3fa1"'
```

- `force` is accepted on this operation only. On any other `DELETE` it is `400` [unsupported-parameter](https://developers.plnnrly.com/en/docs/api/v2/guides/errors/#unsupported-parameter).
- A project with logged time cannot be deleted at all, archived or permanently. The answer is `409` [state-conflict](https://developers.plnnrly.com/en/docs/api/v2/guides/errors/#state-conflict) with `has_logged_time`. Move or delete its worklogs first.

## What can block a delete

When the record's state forbids the delete, the answer is `409` [state-conflict](https://developers.plnnrly.com/en/docs/api/v2/guides/errors/#state-conflict), and `conflict.reason` says why:

| `conflict.reason`   | Means                                                                                                                                  |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `has_logged_time`   | Time has been logged against the record, such as a project or a budget.                                                                |
| `has_children`      | The record still has children, such as a work item with child work items, or a Drive folder that still holds live items or subfolders. |
| `locked_by_review`  | The worklog's hours are under review.                                                                                                  |
| `locked_by_invoice` | The worklog's hours have been invoiced.                                                                                                |
| `locked_by_legacy`  | The sales invoice predates the current invoicing flow.                                                                                 |
| `locked_by_sync`    | The sales invoice has been synced to the accounting system.                                                                            |

Some rules answer `403` [forbidden-by-policy](https://developers.plnnrly.com/en/docs/api/v2/guides/errors/#forbidden-by-policy) instead, such as deleting a work type that work items still use. Each `DELETE` operation page lists what applies to it.

## Who may delete

- Every `DELETE` needs a token with the **Delete** scope for that resource. Without it, the answer is `403` [insufficient-token-scope](https://developers.plnnrly.com/en/docs/api/v2/guides/errors/#insufficient-token-scope). See [Scopes and permissions](https://developers.plnnrly.com/en/docs/api/v2/guides/scopes/).
- Most deletes also need a permission from your role, and each operation page names it. A few depend on the record instead: you may delete your own worklogs and timers, for example.
- Apps signed in with OAuth cannot call a `DELETE`: an OAuth token never holds the **Delete** scope. They can restore, because restoring needs **Write**. See [Sign in with OAuth](https://developers.plnnrly.com/en/docs/api/v2/guides/oauth-apps/).
- AI clients over MCP can delete only while an administrator has switched on **Allow destructive actions**, and only after you confirm the delete. See [Permissions and safety](https://developers.plnnrly.com/en/docs/mcp/permissions-and-safety/).

## Deleting twice

- Deleting an archived record again answers `204`, so retrying an archive is safe. If the retry carries the same `If-Match` and the first attempt went through, it can answer `412` instead: read the record with `trashed=with`, and a `deleted_at` means your delete worked.
- A permanent delete answers `404` the second time. When you retry a delete whose response you lost, treat that `404` as success.
- Send `If-Match` to delete only the version you looked at. See [Concurrency and ETags](https://developers.plnnrly.com/en/docs/api/v2/guides/concurrency-and-etags/).

## Deletions and sync

A permanent delete leaves nothing to read back: a copy you keep in sync notices it only because the record is missing. Archived records stay readable with `trashed=with` and carry `deleted_at`. See [Keeping a copy in sync](https://developers.plnnrly.com/en/docs/api/v2/guides/syncing-data/).

## Related

- [Actions and state changes](https://developers.plnnrly.com/en/docs/api/v2/guides/actions/): how restore and the other actions behave.
- [Scopes and permissions](https://developers.plnnrly.com/en/docs/api/v2/guides/scopes/): the **Delete** scope.
- [Keeping a copy in sync](https://developers.plnnrly.com/en/docs/api/v2/guides/syncing-data/): noticing deletions.
- [Errors and retries](https://developers.plnnrly.com/en/docs/api/v2/guides/errors/): every problem type.
