Deze referentie is alleen in het Engels. Naar het Nederlandse REST API-overzicht

Diese Referenz gibt es nur auf Englisch. Zur deutschen REST-API-Übersicht

Cette référence existe uniquement en anglais. Aller à la présentation de l'API REST en français

Esta referencia solo está en inglés. Ir a la introducción de la API REST en español

Deleting and restoring

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.

View as Markdown

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:

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:

trashedOn a listOn a single read
without (the default)Live records only.An archived record is 404.
withLive and archived records.Live or archived.
onlyArchived 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.

Restore

Terminal
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 does not bring them back: restore each project.
  • Deleting a sales invoice releases the hours that were invoiced on it. Restoring it 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} 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.

Terminal
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.
  • A project with logged time cannot be deleted at all, archived or permanently. The answer is 409 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, and conflict.reason says why:

conflict.reasonMeans
has_logged_timeTime has been logged against the record, such as a project or a budget.
has_childrenThe 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_reviewThe worklog's hours are under review.
locked_by_invoiceThe worklog's hours have been invoiced.
locked_by_legacyThe sales invoice predates the current invoicing flow.
locked_by_syncThe sales invoice has been synced to the accounting system.

Some rules answer 403 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. See Scopes and permissions.
  • 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.
  • 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.

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.

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.