Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions aip/general/0164.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,66 @@ rpc UndeleteBook(UndeleteBookRequest) returns (google.longrunning.Operation) {
be if the RPC was not long-running).
- Both the `response_type` and `metadata_type` fields **must** be specified.

### Expunge

Resources that support soft delete **may** provide an `Expunge` custom method to
allow users to trigger immediate permanent deletion of a ready or soft-deleted
resource. This method can operate on resources that are currently in a ready or
soft-deleted state (e.g., `delete_time` is set).

```proto
// Permanently deletes a soft-deleted Book.
rpc ExpungeBook(ExpungeBookRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {
post: "/v1/{name=publishers/*/books/*}:expunge"
body: "*"
};
option (google.api.method_signature) = "name";
}
```

- The URI must use a custom method with the :expunge suffix.
- The HTTP verb must be POST and the body clause must be "*".
- The response message must be `google.protobuf.Empty` or a `google.longrunning.Operation`.

### Long-running expunge

If the expunge process takes significant time, the method **may** be a
`google.longrunning.Operation` (AIP-151) instead:

```proto
// Permanently deletes a soft-deleted Book.
rpc ExpungeBook(ExpungeBookRequest) returns (google.longrunning.Operation) {
option (google.api.http) = {
post: "/v1/{name=publishers/*/books/*}:expunge"
body: "*"
};
option (google.longrunning.operation_info) = {
response_type: "google.protobuf.Empty"
metadata_type: "OperationMetadata"
};
option (google.api.method_signature) = "name";
}
```

### Expunge request message

Expunge methods implement a common request message pattern:

```proto
message ExpungeBookRequest {
// The name of the soft-deleted book to expunge.
// Format: publishers/{publisher}/books/{book}
string name = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference).type = "library.googleapis.com/Book"
];
}
```

- The request message **must** refer to the resource to be expunged by name.
- There **should not** be any other request fields.

### List and Get

Soft-deleted resources **should not** be returned in `List` (AIP-132) responses
Expand Down Expand Up @@ -139,6 +199,16 @@ If the user calling `Undelete` has proper permission, but the requested
resource is not deleted, the service **must** respond with `ALREADY_EXISTS`
(HTTP 409).

If the user calling `Expunge` requests a resource that does not exist (was never
created or already expunged), the method **must** return `NOT_FOUND` (HTTP 404).

If the resource exists but is not in a ready or soft-deleted state, the method
**must** return `FAILED_PRECONDITION` (HTTP 400).

Standard permission errors (`PERMISSION_DENIED`) apply. Services **must** require
an explicit expunge permission that is separate from standard delete permissions
(e.g., `<service>.<resource>.expunge`).

## Further reading

- For the `Delete` standard method, see AIP-135.
Expand All @@ -148,6 +218,7 @@ resource is not deleted, the service **must** respond with `ALREADY_EXISTS`

## Changelog

- **2026-04-28**: Added guidance for the `Expunge` custom method.
- **2024-09-24**: Included missing requirement for `delete_time`.
- **2023-07-13**: Renamed overloaded `expire_time` to `purge_time`.
- **2021-07-12**: Added error behavior when soft deleting a deleted resource.
Expand Down
Loading