---
title: Work with content approvals
slug: cms-11/dev-docs/working-with-content-approvals
docTags: 
createdAt: 2026-07-22T19:45:21.613Z
---

## Approval definitions

### Create an approval definition

This example shows the creation and saving of a definition:

```csharp
using EPiServer.Approvals.ContentApprovals;

ContentReference contentLink;
IApprovalDefinitionRepository definitionRepository;

var langEN = new CultureInfo[] {
  CultureInfo.GetCultureInfo("en")
};
var langSV = new CultureInfo[] {
  CultureInfo.GetCultureInfo("sv")
};

// Creates a content approval definition
var definition = new ContentApprovalDefinition {
  ContentLink = contentLink,
    Steps = new List<ApprovalDefinitionStep> {
      new ApprovalDefinitionStep("step1", new ApprovalDefinitionReviewer[] {
        new ApprovalDefinitionReviewer("user1a", langEN),
          new ApprovalDefinitionReviewer("user1b", langSV),
      }),
      new ApprovalDefinitionStep("step2", new ApprovalDefinitionReviewer[] {
        new ApprovalDefinitionReviewer("user2a", langEN.Union(langSV))
      })
    }
};

// Saves the definition
await definitionRepository.SaveAsync(definition);
```

### Update an approval definition

This example updates a definition with a new reviewer in the second step. This reviewer can approve items in languages using `CultureInfo.InvariantCulture`.

```csharp
using EPiServer.Approvals.ContentApprovals;

ContentReference contentLink;
IApprovalDefinitionRepository definitionRepository;

var langInvariant = new CultureInfo[] {
  CultureInfo.InvariantCulture
};

// Gets a definition
ApprovalDefinition definition = await definitionRepository.GetAsync(contentLink);
definition = definition.CreateWritableClone();
definition.Steps[1].Reviewers.Add(new ApprovalDefinitionReviewer("user2b", langInvariant));

// Saves a definition
await definitionRepository.SaveAsync(definition);
```

### Add a role to an approval definition

(New in CMS UI 10.10.0)

This example updates a definition with a new role reviewer in the first step. This role can approve items in the Swedish language.

```csharp
using EPiServer.Approvals.ContentApprovals;

ContentReference contentLink;
IApprovalDefinitionRepository definitionRepository;

var langSV = new CultureInfo[] {
  CultureInfo.GetCultureInfo("sv")
};

// Gets a definition
ApprovalDefinition definition = await definitionRepository.GetAsync(contentLink);
definition = definition.CreateWritableClone();
definition.Steps[0].Reviewers.Add(new ApprovalDefinitionReviewer("managers", langSV, ApprovalDefinitionReviewerType.Role));

// Saves a definition
await definitionRepository.SaveAsync(definition);
```

### Delete an approval definition

This example deletes a definition using ID:

```csharp
ApprovalDefinition definition;
IApprovalDefinitionRepository definitionRepository;

// Deletes a definition
await definitionRepository.DeleteAsync(definition.ID);
```

:::hint{type="info"}
**Note**
A definition cannot be deleted if running approvals exist. All approval instances must be completed or aborted before deleting an approval definition.
:::

### Get an approval definition

Gets a definition in a couple of different ways:

```csharp
using EPiServer.Approvals.ContentApprovals;

ContentReference contentLink;
ApprovalDefinition definition;
Approval approval;
IApprovalDefinitionRepository definitionRepository;
IApprovalDefinitionVersionRepository definitionVersionRepository;

// Gets the latest version of a definition using a definition id.
var definition1 = await definitionRepository.GetAsync(definition.ID);

// Gets a specific version of a definition using a version id.
var definition2 = await definitionVersionRepository.GetAsync(approval.VersionID);

// Gets the latest version of a definition using a ContentReference. 
var definition3 = await definitionRepository.GetAsync(contentLink);

// Gets the latest version of a definition by resolving a ContentReference.  
var definitionResolveResult = await definitionRepository.ResolveAsync(contentLink);
// The Resolve-method returns a result with a definition and a flag specifying if the definition was found on an ancestor
var definition4 = definitionResolveResult.Definition as ContentApprovalDefinition;
var isInherited = definitionResolveResult.IsInherited;
```

## Approvals

`IApprovalRepository` is the base interface for saving, deleting, and listing approvals and their decisions. `IApprovalEngine` is built on top of `IApprovalRepository` to streamline the handling of approvals. Use the repository for listing approvals and use the engine for handling approvals. The engine also raises events that you can hook up in `IApprovalEngineEvents`.

### Hook up events

```csharp
[InitializableModule]
public class ApprovalLogger: IInitializableModule {
  private IApprovalEngineEvents _approvalEngineEvents;

  public void Initialize(InitializationEngine context) {
    _approvalEngineEvents = context.Locate.Advanced.GetInstance<IApprovalEngineEvents>();
    _approvalEngineEvents.Approved += OnApproved;
  }

  public void Uninitialize(InitializationEngine context) => _approvalEngineEvents.Approved -= OnApproved;

  private void OnApproved(ApprovalEventArgs e) => LogManager.GetLogger(typeof (ApprovalLogger)).Debug("Approve");
}
```

### Start an approval

You do not start a content approval by saving an approval but by saving a content item with `SaveAction.RequestApproval`, which automatically creates and saves a `ContentApproval` for this content item, if you can resolve a definition.

```csharp
IContent content;
IContentRepository contentRepository;
    
var approvalContentLink = contentRepository.Save(content, SaveAction.RequestApproval);
```

### Abort an approval

This example deletes an approval and raises an aborted event:

```csharp
Approval approval;
IApprovalEngine approvalEngine;
    
await approvalEngine.AbortAsync(approval.ID, "user");
```

### Make a decision

Use the engine to approve or reject a step or the whole approval. You can add an optional comment to explain the reason for the decision. If the decision finishes the approval (approves the last step or rejects a step), it saves this comment on the approval as `CompletedComment`.

```csharp
Approval approval;
IApprovalEngine approvalEngine;
    
// Approve a step if user is part of the current definition step
await approvalEngine.ApproveAsync(approval.ID, "user", 1, ApprovalDecisionScope.Step);
    
// Reject a step if user is part of the current definition step, adding a comment
await approvalEngine.RejectAsync(approval.ID, "user", 1, ApprovalDecisionScope.Step, "This is why I did it");
    
// Force approve a step whether the user is part of the current definition step or not
await approvalEngine.ApproveAsync(approval.ID, "user", 1, ApprovalDecisionScope.ForceStep);
    
// Force approve the whole approval 
await approvalEngine.ApproveAsync(approval.ID, "user", 1, ApprovalDecisionScope.Force);
```

There are also several more explicitly named extension methods to the engine that can be used, for example,`ApproveStepAsync `and `ForceApproveAsync`.

### List approvals

Use the `GetAsync`  or  `GetItemsAsync` methods on `IApprovalRepository` to get specific approvals using ID or `ContentReference`:

```csharp
using EPiServer.Approvals.ContentApprovals;
    
ContentReference contentLink;
IApprovalRepository approvalRepository;
   
var approval = await approvalRepository.GetAsync(contentLink);
```

The ListAsync method takes an `ApprovalQuery`  or  `ContentApprovalQuery` object which searches the approvals based on the specified query-data and returns a filtered list. You can page this list.

This example gets approvals for review by a user based on a specific definition.

```csharp
using EPiServer.Approvals.ContentApprovals;

ContentApprovalDefinition definition;
IApprovalRepository approvalRepository;

var approvals = await approvalRepository.ListAsync(new ContentApprovalQuery {
  Username = "user",
    Status = ApprovalStatus.InReview,
    DefinitionID = definition.ID
});
```

This example list decisions made for approval:

```csharp
Approval approval;
IApprovalRepository approvalRepository;
    
var decisions = await approvalRepository.ListDecisionsAsync(approval.ID);
```
