# Discourse Policy

**URL:** https://meta.discourse.org/t/discourse-policy/88557
**Category:** Plugin
**Tags:** official, policy, included-in-core
**Created:** [May 28, 2018, 1:26am UTC](https://meta.discourse.org/t/discourse-policy/88557 "2018-05-28T01:26:03Z")
**Posts on this page:** 15
**Page:** 1

<div class="post-metadata">

### Author: ![Discourse](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/discourse/32/148734_2.png) [@Discourse](https://meta.discourse.org/u/Discourse)
#### Post date: [May 28, 2018, 1:26am UTC](https://meta.discourse.org/t/discourse-policy/88557/1 "2018-05-28T01:26:03Z")

</div>

| | | |
| --- | --- | --- |
| :discourse2: | **Summary** | **Discourse Policy** gives the ability to confirm your users have seen or done something, with optional reminders. |
| 📖 | **Install Guide** | **This plugin is bundled with Discourse core**. There is no need to install the plugin separately. |

> [@](#):
>
> :discourse2: As this is an #official plugin maintained by the Discourse team, #Support, #Contribute > Bug, #Contribute > UX, and #Contribute > Feature requests can be made in the respective categories here on Meta, and tagged with the appropriate plugin tag. Click on a link below to get one started. 👍
> 
> [❓&nbsp; **Support**](https://meta.discourse.org/new-topic?category_id=6&body=%3E%20Before%20asking,%20did%20you%20search%20first%3F%20Press%20%F0%9F%94%8D%20at%20the%20upper%20right%20to%20search.&tags=policy "Ask for support on configuring and using Discourse Policy") [🐛&nbsp; **Bug**](https://meta.discourse.org/new-topic?category_id=1&tags=policy "A bug report means something is broken, preventing normal/typical use of the plugin") [👀&nbsp; **UX**](https://meta.discourse.org/new-topic?category_id=9&tags=policy "Discussion about the user interface of Discourse Policy, and how features are presented (including language and UI elements)") [💡&nbsp; **Feature**](https://meta.discourse.org/new-topic?category_id=2&tags=policy "Discussion about how existing Discourse Policy features can be improved or enhanced, and how proposed new features could work")

### Features

Discourse policy shares much of its DNA with polls, it has the goal of ensuring members of a group **accept** certain policies by clicking a button.

It has some specific features that set it apart from polls:

1. It **must** apply to a group.

2. It can optionally **nag** members of a group either weekly or daily to **click** accept on a policy.

3. The UX makes it very easy to see who accepted and who did not accept a policy.

### Configuration

#### Screenshots

Before clicking accept

 ![image](https://global.discourse-cdn.com/meta/original/3X/5/e/5e17602e75709311850da6e6e45cf5aca3619763.png)

After clicking “grey” person on top right

 ![image](https://global.discourse-cdn.com/meta/original/3X/8/5/8587bb3f0695b9c5c83a6eb33ba142de8abed456.png)

After clicking accept

 ![image](https://global.discourse-cdn.com/meta/original/3X/d/5/d5c6ed7c752bfadaf1393c5418d86e594b0a2d08.png)

#### Creating a policy

Discourse policy registers a custom `[policy]` BBCode element. Which means you need to write this block of code in your post where you want to show this policy.

```plaintext
[policy group=founders reminder=daily]
I accept this policy will annoy me daily until I click accept
[/policy]

```

In this case the policy applies to the `founders` group and a reminder is sent **daily** to all users that did not accept the policy.

#### Site settings

`policy_enabled` : Enable the discourse policy plugin on the site.

`policy_restrict_to_staff_posts`: Policies may only appear on staff posts.

`policy easy revoke`: Show the accept and revoke buttons at the same time.

#### BBCode attributes

The `[policy]` element can accept the following attributes.

- **group** : the group name that policy will apply to **required**
- **version** : the version number of the policy, bump number up to require all users re-accept the policy
- **reminder** : remind users of the group to accept policy (weekly or daily), optional, default off
- **accept** : text used for accept button, default is “Accept Policy”
- **revoke** : text used for revoke button, default is “Revoke Policy”
- **renew** : number of days or text describing period (monthly, quarterly or yearly) after which a user needs to re-accept policy
- **renew-start** : the first date renewals will start at Eg: `17-10-2017`, useful if you want to set the renewal cadence so it happens every half-year (Jan,June)
- **add-users-to-group** : accepts a single group as input; users who accept the policy will be automatically added into this group (when policy is manually revoked or if the policy version is updated, user(s) gets removed from group)
- **private** : hide users who accepted/did not accept the policy

> ℹ The backend job to [check for policy reminders](https://github.com/discourse/discourse-policy/blob/main/jobs/scheduled/check_policy.rb) runs every 6 hours. If the `reminder` element is set to weekly or daily, users who have not accepted a policy will be sent notification reminders to accept the policy _only if_ they have not already received a reminder notification for the policy within the past week or day.

#### Usage examples

Basic policy:

```
[policy group="team" reminder="daily" accept="Accept" revoke="Ignore" version="1"]
I have read, understand and acknowledge this policy.
[/policy]

```

Bumping the revision number, to require that everyone reads the update:

```
[policy group="team" reminder="daily" accept="Accept" revoke="Ignore" version="2"]
I have read, understand and acknowledge this policy. (Last updated [date=2020-04-23 timezone="America/Los_Angeles"])
[/policy]

```

Periodic auditing task:

```
[policy group="audit_committee" reminder="weekly" renew="90" renew-start="01-07-2019" version="1" accept="Assignment Complete"] 
I have ensured that someone is assigned to this quarterly audit task.
[/policy]

```

### CHANGELOG

### TODO

- Optional immediate reminder for new policies
- Possibly allow certain groups to apply policies (expanding on staff)

> Last edited by @david 2025-07-22T13:14:42Z
> 
> > **Check document**
> >
> > Perform check on document:

---

<div class="post-metadata">

### Author: ![JammyDodger](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/jammydodger/32/254611_2.png) [@JammyDodger](https://meta.discourse.org/u/JammyDodger)
#### Post date: [June 17, 2023, 12:16pm UTC](https://meta.discourse.org/t/discourse-policy/88557/71 "2023-06-17T12:16:04Z")

</div>

2 posts were split to a new topic: [Limit Policy creation to just staff?](https://meta.discourse.org/t/limit-policy-creation-to-just-staff/268731)

---

<div class="post-metadata">

### Author: ![JammyDodger](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/jammydodger/32/254611_2.png) [@JammyDodger](https://meta.discourse.org/u/JammyDodger)
#### Post date: [June 17, 2023, 12:25pm UTC](https://meta.discourse.org/t/discourse-policy/88557/73 "2023-06-17T12:25:36Z")

</div>

4 posts were split to a new topic: [Is it possible to disallow revoking agreement to certain policies?](https://meta.discourse.org/t/is-it-possible-to-disallow-revoking-agreement-to-certain-policies/268732)

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [June 5, 2023, 12:45pm UTC](https://meta.discourse.org/t/discourse-policy/88557/77 "2023-06-05T12:45:32Z")

</div>

It looks like the 200 limit and the `policy_max_group_size` got removed with this commit. Am I missing something?

[https://github.com/discourse/discourse-policy/pull/43](https://github.com/discourse/discourse-policy/pull/43)

---

<div class="post-metadata">

### Author: ![JammyDodger](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/jammydodger/32/254611_2.png) [@JammyDodger](https://meta.discourse.org/u/JammyDodger)
#### Post date: [June 17, 2023, 12:11pm UTC](https://meta.discourse.org/t/discourse-policy/88557/78 "2023-06-17T12:11:52Z")

</div>

6 posts were split to a new topic: [People receiving reminders even though they’ve accepted the policy](https://meta.discourse.org/t/people-receiving-reminders-even-though-theyve-accepted-the-policy/268730)

---

<div class="post-metadata">

### Author: ![hellekin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/hellekin/32/51636_2.png) [@hellekin](https://meta.discourse.org/u/hellekin)
#### Post date: [October 9, 2024, 5:30pm UTC](https://meta.discourse.org/t/discourse-policy/88557/80 "2024-10-09T17:30:53Z")

</div>

When a policy with long button text is applied to a large group, the UI gets mangled: in the screenshot below, we can see that the avatars form an _near infinite_ column that makes it a bit akward. I will look into the CSS to see if that can be easily fixed, but probably there should be some kind of redesign:

 ![image](https://global.discourse-cdn.com/meta/original/4X/b/c/a/bca577b865e4a4a4bb8f8fb9bfe965f182fbb59e.png)

Removing `display: flex` on `.cooked .policy .user-lists` makes it look fine on Desktop:

 ![image](https://global.discourse-cdn.com/meta/original/4X/2/4/5/245d58253c8fd9beee6c5407fafcd1091c3d515f.png)

I ended up using the following CSS:

```css
/* Make policy user list not overflow */
.cooked .policy .user-lists {
    display: block;
}

```

---

<div class="post-metadata">

### Author: ![ishua\_wang](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ishua_wang/32/477584_2.png) [@ishua\_wang](https://meta.discourse.org/u/ishua_wang)
#### Post date: [January 2, 2025, 3:42am UTC](https://meta.discourse.org/t/discourse-policy/88557/81 "2025-01-02T03:42:35Z")

</div>

Error after installing the plugin. Don’t know why. Something related “while trying to load [/admin/dashboard/general.json](https://forum.wangyx.com/admin/dashboard/general.json)”.

Server Error

while trying to load [/admin/plugins]

Error code: 500 error

# Oops

The software powering this discussion forum encountered an unexpected problem. We apologize for the inconvenience.

Detailed information about the error was logged, and an automatic notification generated. We’ll take a look at it.

No further action is necessary. However, if the error condition persists, you can provide additional detail, including steps to reproduce the error, by posting a discussion topic in the site’s feedback category.

---

<div class="post-metadata">

### Author: ![ppcole](https://avatars.discourse-cdn.com/v4/letter/p/49beb7/32.png) [@ppcole](https://meta.discourse.org/u/ppcole)
#### Post date: [February 23, 2025, 7:45pm UTC](https://meta.discourse.org/t/discourse-policy/88557/82 "2025-02-23T19:45:48Z")

</div>

Hello,

Will the Accept/Revoke Buttons show if the Policy is in a Category Topic that is Closed so it doesn’t allow Replies?

Thanks

---

<div class="post-metadata">

### Author: ![ondrej](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ondrej/32/198804_2.png) [@ondrej](https://meta.discourse.org/u/ondrej)
#### Post date: [February 23, 2025, 9:35pm UTC](https://meta.discourse.org/t/discourse-policy/88557/83 "2025-02-23T21:35:52Z")

</div>

I haven’t used this plugin in a while however I think that it will allow users to accept/revoke when a topic is closed since pressing a button is not a reply which is what closing prevents.

---

<div class="post-metadata">

### Author: ![Iheb\_ben\_aicha](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/iheb_ben_aicha/32/482372_2.png) [@Iheb\_ben\_aicha](https://meta.discourse.org/u/Iheb_ben_aicha)
#### Post date: [June 3, 2025, 10:29am UTC](https://meta.discourse.org/t/discourse-policy/88557/84 "2025-06-03T10:29:27Z")

</div>

hi i have a question :  
We want to use the Discourse Policy plugin to : **force first-time users to accept Terms of Service (ToS)**. Requirements:

- If they don’t accept → they cannot use the forum (only see ToS page)
- When they accept → they can use the forum + get a “ToS” badge

my solution was to create a new subject and within i added this :  
[policy group=trust\_level\_0 reminder=daily add-users-to-group=tos\_accepted accept=“I Accept Terms” version=“1”]  
By clicking “I Accept Terms”, I confirm that I have read and agree to the Terms of Service.  
[/policy]

but if a new user logs in the policy doesn’t show up at all and he can do whatever he wants we want only if he click on “Accept” to be able to access our page . how to do that and thanks

---

<div class="post-metadata">

### Author: ![ondrej](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ondrej/32/198804_2.png) [@ondrej](https://meta.discourse.org/u/ondrej)
#### Post date: [June 3, 2025, 11:12am UTC](https://meta.discourse.org/t/discourse-policy/88557/85 "2025-06-03T11:12:02Z")

</div>

You could use a custom field and link the ToS and FaQ page. Once a user reads them they will receive the ‘Read Guidelines’ bagde.

> [@How to force existing users to accept ToS](https://meta.discourse.org/t/how-to-force-existing-users-to-accept-tos/243878/9):
>
> To require acceptance of a terms-of-service, use a required custom user field. When defining a user field, you can select “Field Requirement” as “For all users”. When you do so, all new and existing users will be prompted to fill the custom field.

---

<div class="post-metadata">

### Author: ![Iheb\_ben\_aicha](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/iheb_ben_aicha/32/482372_2.png) [@Iheb\_ben\_aicha](https://meta.discourse.org/u/Iheb_ben_aicha)
#### Post date: [June 3, 2025, 1:49pm UTC](https://meta.discourse.org/t/discourse-policy/88557/86 "2025-06-03T13:49:58Z")

</div>

Thank you for your response — it worked just as you said. However, what I really want is to display this banner after the user logs in, and only allow access to the page if the user clicks ‘Accept’. If not, the banner should remain visible, and the user shouldn’t be able to interact with the page

 ![The image is a message interface displaying a dismissive acceptance of a policy, accompanied by a cat wearing a top hat avatars and vote counts. (Captioned by AI)](https://global.discourse-cdn.com/meta/original/4X/3/4/1/341ac5d079734ac0e8725c514ef83deb646099ee.png)

what i have right now is this :

 ![13](https://global.discourse-cdn.com/meta/original/4X/b/7/7/b778bcd7b27c7a06d73b592ec7745f592b99a77b.jpeg)  
after that the user can access our page normally

is there any way to do that and thanks

---

<div class="post-metadata">

### Author: ![david](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/david/32/157490_2.png) [@david](https://meta.discourse.org/u/david)
#### Post date: [July 22, 2025, 1:14pm UTC](https://meta.discourse.org/t/discourse-policy/88557/87 "2025-07-22T13:14:50Z")

</div>

🥳 This plugin is now bundled with Discourse core as part of [Bundling more popular plugins with Discourse core](https://meta.discourse.org/t/bundling-more-popular-plugins-with-discourse-core/373574). If you are self-hosting and use the plugin, you need to remove it from your `app.yml` before your next upgrade.

---

<div class="post-metadata">

### Author: ![jdc20181](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/jdc20181/32/515028_2.png) [@jdc20181](https://meta.discourse.org/u/jdc20181)
#### Post date: [July 28, 2025, 7:09pm UTC](https://meta.discourse.org/t/discourse-policy/88557/88 "2025-07-28T19:09:43Z")

</div>

It would be nice if you could add the ability to _remove_ users from a group upon _acceptance or rejection._

As of now this feature only nags users (optionally) and adds - unless I am missing something.

Thanks!

---

<div class="post-metadata">

### Author: ![kiefferr](https://avatars.discourse-cdn.com/v4/letter/k/a5b964/32.png) [@kiefferr](https://meta.discourse.org/u/kiefferr)
#### Post date: [July 27, 2026, 7:25pm UTC](https://meta.discourse.org/t/discourse-policy/88557/89 "2026-07-27T19:25:53Z")

</div>

This is great, but where it falls short is a more obvious and visible way to deliver the reminders for everyone who hasn’t agreed to the policy. It almost needs to be a popup reminder rather than a simple notification that most users will never see tucked away behind their profile icon.

Here’s a query that will identify all active users in the group who have not agreed. Useful if extra followup is needed.

`WITH target_policy AS (`  
`-- Locate the master policy ID linked to your topic`  
`SELECT pp.id AS post_policy_id`  
`FROM post_policies pp`  
`JOIN posts p ON p.id = pp.post_id`  
`WHERE p.topic_id = YOUR_TOPIC_ID`  
`LIMIT 1`  
`),`  
`any_accepted_users AS (`  
`-- Gather every user ID associated with this policy log row`  
`-- Bypassing version checks prevents old signatures from skewing the results`  
`SELECT DISTINCT pu.user_id`  
`FROM policy_users pu`  
`JOIN target_policy tp ON tp.post_policy_id = pu.post_policy_id`  
`WHERE pu.accepted_at IS NOT NULL`  
`)`  
`SELECT`  
`u.id AS user_id,`  
`u.username,`  
`u.name,`  
`ue.email,`  
`u.last_seen_at`  
`FROM users u`  
`-- Safely grab the primary email account`  
`LEFT JOIN user_emails ue ON ue.user_id = u.id AND ue.primary = true`  
`-- Restrict target pool strictly to a certain group`  
`JOIN group_users gu ON gu.user_id = u.id`  
`JOIN groups g ON g.id = gu.group_id`  
`WHERE g.name = 'YOUR_GROUP'`  
`AND u.active = true`  
`AND u.id > 0 -- Exclude default system/bot accounts`  
`-- Hard-drop any user ID present in the policy log database block`  
`AND u.id NOT IN (SELECT user_id FROM any_accepted_users)`  
`ORDER BY u.last_seen_at DESC`
