[GH-ISSUE #722] [Feature Request] Disable topic subscriptions through admin API #526

Closed
opened 2026-05-07 00:25:05 +02:00 by BreizhHardware · 16 comments

Originally created by @CreativeWarlock on GitHub (May 12, 2023).
Original GitHub issue: https://github.com/binwiederhier/ntfy/issues/722

Hello mate,

For a small startup business that a friend and I have been creating and which would integrate your cool notication service, we got a little feature request for NTFY.sh that allows managing user subscriptions through a given admin user, preferrably via POST requests.

The following diagram shows a simplified model of our business, where users pay for accessing signal services that send data over different NTFY topics (represented by Channel A, B, C).

Topics_For_Subscriptions-Seite-2 drawio

For the case that our customers cancel their subcriptions with one of our products, we need something like a flag that can be toggled on/off via admin user (or however you think it best to realize) to ensure that messages are no longer forwarded to a user's endpoint (phone).

This would be beneficial to keep the number of topics small (T=1 topic for N users) rather than setting up individual topics, resulting in T x N topics. It would also reduce the customer's time to setup the topics on the phone with ideally short topic names.

Would this be technically easy to realize? Let me know if you need additional information.

Kind regards
Marcel

Originally created by @CreativeWarlock on GitHub (May 12, 2023). Original GitHub issue: https://github.com/binwiederhier/ntfy/issues/722 Hello mate, For a small startup business that a friend and I have been creating and which would integrate your cool notication service, we got a little feature request for NTFY.sh that allows managing user subscriptions through a given admin user, preferrably via POST requests. The following diagram shows a simplified model of our business, where users pay for accessing signal services that send data over different NTFY topics (represented by Channel A, B, C). ![Topics_For_Subscriptions-Seite-2 drawio](https://github.com/binwiederhier/ntfy/assets/4894323/07b573c8-7a40-42bd-a2c6-c9f3ec57c234) For the case that our customers cancel their subcriptions with one of our products, we need something like a flag that can be toggled on/off via admin user (or however you think it best to realize) to ensure that messages are no longer forwarded to a user's endpoint (phone). This would be beneficial to keep the number of topics small (T=1 topic for N users) rather than setting up individual topics, resulting in T x N topics. It would also reduce the customer's time to setup the topics on the phone with ideally short topic names. Would this be technically easy to realize? Let me know if you need additional information. Kind regards Marcel
BreizhHardware 2026-05-07 00:25:05 +02:00
Author
Owner

@binwiederhier commented on GitHub (May 13, 2023):

This video shows a new endpoint v1/access that lets you control the access control list via an API (admin user only!): https://docs.ntfy.sh/config/#access-control

In the example, use phil is an admin, and is using the API to create an ACL entry for user phil1 (which is not an admin). After the ACL entry is created, phil1 can subscribe to topic gold. After the ACL entry is removed, the user's connection is killed as the access is revoked. Other connections (here: user phil is still subscribed) stay intact.

Create ACL entry (similar to ntfy access command):

curl -s -u phil:phil -d '{"username":"phil1", "topic":"gold","permission":"rw"}' localhost:2586/v1/access

Remove ACL entry:

curl -XDELETE -s -u phil:phil -d '{"username":"phil1", "topic":"gold"}' localhost:2586/v1/access

Screencast_00067.webm

<!-- gh-comment-id:1546721889 --> @binwiederhier commented on GitHub (May 13, 2023): This video shows a new endpoint `v1/access` that lets you control the access control list via an API (admin user only!): https://docs.ntfy.sh/config/#access-control In the example, use `phil` is an admin, and is using the API to create an ACL entry for user `phil1` (which is not an admin). After the ACL entry is created, `phil1` can subscribe to topic `gold`. After the ACL entry is removed, the user's connection is killed as the access is revoked. Other connections (here: user `phil` is still subscribed) stay intact. Create ACL entry (similar to `ntfy access` command): ``` curl -s -u phil:phil -d '{"username":"phil1", "topic":"gold","permission":"rw"}' localhost:2586/v1/access ``` Remove ACL entry: ``` curl -XDELETE -s -u phil:phil -d '{"username":"phil1", "topic":"gold"}' localhost:2586/v1/access ``` [Screencast_00067.webm](https://github.com/binwiederhier/ntfy/assets/664597/afcf2d2d-794c-4159-9050-a1025658256b)
Author
Owner

@binwiederhier commented on GitHub (May 13, 2023):

WIP PR: https://github.com/binwiederhier/ntfy/pull/727

<!-- gh-comment-id:1546728077 --> @binwiederhier commented on GitHub (May 13, 2023): WIP PR: https://github.com/binwiederhier/ntfy/pull/727
Author
Owner

@binwiederhier commented on GitHub (May 14, 2023):

I added the following endpoints that can be used as admin user:

  • GET v1/users - List users and user grants (ACL)
  • PUT v1/users - Create a non-admin user and add to tier
  • DELETE v1/users - Delete non-admin user
  • PUT v1/users/access - Add/update ACL entry for a user
  • DELETE v1/users/access Delete ACL entry for a user

Here's an example flow using the new endpoints (updated May 15)

  1. Add user "marcus"
  2. Grant access to topic "gold" + "silver"
  3. List users and user grants
  4. Subscribe to topics "gold" + "silver"
  5. Revoke access from topic "silver" (kills silver topic connection)
  6. Remove user "marcus" (kills gold topic connection)

1. Add user

curl \
  -X PUT \
  -u admin:adminpass \
  -d '{"username":"marcus","password":"marcuspass","tier":"business"}' \
  localhost:2586/v1/users

2. Grant read only access

curl \
  -X POST \
  -u admin:adminpass \
  -d '{"username":"marcus", "topic":"gold","permission":"ro"}' \
  localhost:2586/v1/users/access 
curl \
  -X POST \
  -u admin:adminpass \
  -d '{"username":"marcus", "topic":"silver","permission":"ro"}' \
  localhost:2586/v1/users/access 

3. List users and user grants

curl -su phil:phil localhost:2586/v1/users | jq .                                                                
[
  {
    "username": "phil",
    "role": "admin",
    "tier": "business"   
  },
  {
    "username": "marcus",
    "role": "user",
    "tier": "business",
    "grants": [
      {
        "topic": "gold",
        "permission": "read-only"
      },
      {
        "topic": "silver",
        "permission": "read-only"
      }
    ]
  },
  {
    "username": "*",
    "role": "anonymous"
  }
]

4. Subscribe as "marcus"

curl -u marcus:marcuspass localhost:2586/gold/json   # Connection 1
curl -u marcus:marcuspass localhost:2586/silver/json # Connection 2

5. Revoke access from "silver" (kills connection 2)

curl \
  -X DELETE \
  -u admin:adminpass \
  -d '{"username":"marcus", "topic":"silver"}' \
  localhost:2586/v1/users/access

curl -su phil:phil localhost:2586/v1/users | jq .                                                                
[
  ...
  {
    "username": "marcus",
    "role": "user",
    "tier": "business",
    "grants": [
      {
        "topic": "gold",
        "permission": "read-only"
      }
    ]
  },
  ...
]

6. Delete user "marcus" (kills connection 1)

curl \
  -X DELETE \
  -u admin:adminpass \
  -d '{"username":"marcus"}' \
  localhost:2586/v1/users
<!-- gh-comment-id:1546789275 --> @binwiederhier commented on GitHub (May 14, 2023): I added the following endpoints that can be used as admin user: - `GET v1/users` - List users and user grants (ACL) - `PUT v1/users` - Create a non-admin user and add to tier - `DELETE v1/users` - Delete non-admin user - `PUT v1/users/access` - Add/update ACL entry for a user - `DELETE v1/users/access` Delete ACL entry for a user Here's an example flow using the new endpoints (updated May 15) 1. Add user "marcus" 2. Grant access to topic "gold" + "silver" 3. List users and user grants 4. Subscribe to topics "gold" + "silver" 5. Revoke access from topic "silver" (kills silver topic connection) 6. Remove user "marcus" (kills gold topic connection) ## 1. Add user ``` curl \ -X PUT \ -u admin:adminpass \ -d '{"username":"marcus","password":"marcuspass","tier":"business"}' \ localhost:2586/v1/users ``` ## 2. Grant read only access ``` curl \ -X POST \ -u admin:adminpass \ -d '{"username":"marcus", "topic":"gold","permission":"ro"}' \ localhost:2586/v1/users/access curl \ -X POST \ -u admin:adminpass \ -d '{"username":"marcus", "topic":"silver","permission":"ro"}' \ localhost:2586/v1/users/access ``` ## 3. List users and user grants ``` curl -su phil:phil localhost:2586/v1/users | jq . [ { "username": "phil", "role": "admin", "tier": "business" }, { "username": "marcus", "role": "user", "tier": "business", "grants": [ { "topic": "gold", "permission": "read-only" }, { "topic": "silver", "permission": "read-only" } ] }, { "username": "*", "role": "anonymous" } ] ``` ## 4. Subscribe as "marcus" ``` curl -u marcus:marcuspass localhost:2586/gold/json # Connection 1 curl -u marcus:marcuspass localhost:2586/silver/json # Connection 2 ``` ## 5. Revoke access from "silver" (kills connection 2) ``` curl \ -X DELETE \ -u admin:adminpass \ -d '{"username":"marcus", "topic":"silver"}' \ localhost:2586/v1/users/access curl -su phil:phil localhost:2586/v1/users | jq . [ ... { "username": "marcus", "role": "user", "tier": "business", "grants": [ { "topic": "gold", "permission": "read-only" } ] }, ... ] ``` ## 6. Delete user "marcus" (kills connection 1) ``` curl \ -X DELETE \ -u admin:adminpass \ -d '{"username":"marcus"}' \ localhost:2586/v1/users ```
Author
Owner

@binwiederhier commented on GitHub (May 14, 2023):

Sounds good?

<!-- gh-comment-id:1546789401 --> @binwiederhier commented on GitHub (May 14, 2023): Sounds good?
Author
Owner

@CreativeWarlock commented on GitHub (May 14, 2023):

Going to test your fast solution after my vacations; I'll be back on Sunday evening, so probably Monday-Tuesday I can test it :)

<!-- gh-comment-id:1547017250 --> @CreativeWarlock commented on GitHub (May 14, 2023): Going to test your fast solution after my vacations; I'll be back on Sunday evening, so probably Monday-Tuesday I can test it :)
Author
Owner

@binwiederhier commented on GitHub (May 15, 2023):

Updated the endpoints and examples again. I think I'm happy with this and will merge it as-is. Given that it won't be released, we can still change it if you like @CreativeWarlock. Enjoy your time off.

<!-- gh-comment-id:1548024931 --> @binwiederhier commented on GitHub (May 15, 2023): Updated the endpoints and examples again. I think I'm happy with this and will merge it as-is. Given that it won't be released, we can still change it if you like @CreativeWarlock. Enjoy your time off.
Author
Owner

@binwiederhier commented on GitHub (May 18, 2023):

This is now part of v2.5.0. I'll leave the ticket pending confirmation that this is ok.

<!-- gh-comment-id:1553527835 --> @binwiederhier commented on GitHub (May 18, 2023): This is now part of v2.5.0. I'll leave the ticket pending confirmation that this is ok.
Author
Owner

@CreativeWarlock commented on GitHub (May 24, 2023):

The solution seems to fulfill our needs perfectly! I'll update NTFY on my server and need to implement these ACL commands in our backend services. From the looks of your example, I would expect it will work right away. Will report back soon.
So excited! 😄

<!-- gh-comment-id:1561159329 --> @CreativeWarlock commented on GitHub (May 24, 2023): The solution seems to fulfill our needs perfectly! I'll update NTFY on my server and need to implement these ACL commands in our backend services. From the looks of your example, I would expect it will work right away. Will report back soon. So excited! 😄
Author
Owner

@CreativeWarlock commented on GitHub (May 24, 2023):

Small typo found in "1. Add User:"
curl -X PUT -u admin:adminpass -d '{"username":"marcus","password":"marcuspass","tier":"business"}' localhost:2586/v1/users
Response:

{
    "code": 40030,
    "http": 400,
    "error": "invalid request: tier does not exist"
}

('topic' was probably meant, but it's not being used in user creation which is fine as it is 👍 )

All other "CRUD" operations work fine.

<!-- gh-comment-id:1561268783 --> @CreativeWarlock commented on GitHub (May 24, 2023): Small typo found in "1. Add User:" curl -X PUT -u admin:adminpass -d '{"username":"marcus","password":"marcuspass","tier":"business"}' localhost:2586/v1/users Response: ``` { "code": 40030, "http": 400, "error": "invalid request: tier does not exist" } ``` ('topic' was probably meant, but it's not being used in user creation which is fine as it is 👍 ) All other "CRUD" operations work fine.
Author
Owner

@CreativeWarlock commented on GitHub (May 24, 2023):

I guess in the app on the smartphone, our customers have to add their user name somewhere so that messages can be received on the topics they are subscribed / paying for.

However, if I go to
'settings' -> 'general' -> 'manage users'
and add a new user (I previously created 'hans' and gave it RO permission to some topics), I cannot "create" that new user:

image

Any ideas what I might've done wrong?

<!-- gh-comment-id:1561291182 --> @CreativeWarlock commented on GitHub (May 24, 2023): I guess in the app on the smartphone, our customers have to add their user name somewhere so that messages can be received on the topics they are subscribed / paying for. However, if I go to 'settings' -> 'general' -> 'manage users' and add a new user (I previously created 'hans' and gave it RO permission to some topics), I cannot "create" that new user: ![image](https://github.com/binwiederhier/ntfy/assets/4894323/8bee4223-2326-4364-8d7b-3ff1664bffca) Any ideas what I might've done wrong?
Author
Owner

@binwiederhier commented on GitHub (May 24, 2023):

Small typo found in "1. Add User:" curl -X PUT -u admin:adminpass -d '{"username":"marcus","password":"marcuspass","tier":"business"}' localhost:2586/v1/users Response:

{
    "code": 40030,
    "http": 400,
    "error": "invalid request: tier does not exist"
}

('topic' was probably meant, but it's not being used in user creation which is fine as it is +1 )

This seems correct to me. You are creating a user in this API call, and are assigning the tier business to them. If that tier does not exist, the endpoint will error out. That tier parameter is optional.

User creation does not relate to topics at all. The flow is: (a) you first create a user using v1/users, and then you (b) grant access to a topic using v1/users/access.

However, if I go to 'settings' -> 'general' -> 'manage users' and add a new user (I previously created 'hans' and gave it RO permission to some topics), I cannot "create" that new user

The user management in the Android app is subpar. I will work on that in the near future. For now, you can add an existing (!) user like you said, but you cannot create a user in the Android app. That is correct. I may add that feature later, but it won't help your use case, IMHO.

<!-- gh-comment-id:1561400661 --> @binwiederhier commented on GitHub (May 24, 2023): > Small typo found in "1. Add User:" curl -X PUT -u admin:adminpass -d '{"username":"marcus","password":"marcuspass","tier":"business"}' localhost:2586/v1/users Response: > > ``` > { > "code": 40030, > "http": 400, > "error": "invalid request: tier does not exist" > } > ``` > > ('topic' was probably meant, but it's not being used in user creation which is fine as it is +1 ) This seems correct to me. You are creating a user in this API call, and are assigning the [tier](https://docs.ntfy.sh/config/#tiers) `business` to them. If that tier does not exist, the endpoint will error out. That `tier` parameter is optional. User creation does not relate to topics at all. The flow is: (a) you first create a user using `v1/users`, and then you (b) grant access to a topic using `v1/users/access`. > However, if I go to 'settings' -> 'general' -> 'manage users' and add a new user (I previously created 'hans' and gave it RO permission to some topics), I cannot "create" that new user The user management in the Android app is subpar. I will work on that in the near future. For now, you can add an existing (!) user like you said, but you cannot create a user in the Android app. That is correct. I may add that feature later, but it won't help your use case, IMHO.
Author
Owner

@CreativeWarlock commented on GitHub (May 24, 2023):

Thanks for the link to tiers. I have obviously overlooked that feature in your docs.

Yes sadly, if users can't connect to nfty topics through the credentials they get from our system, i.e. after they register & subscribe to our product(s), the business case is not functional and we cannot move on.
Let me know if you need additional infos.

<!-- gh-comment-id:1561789603 --> @CreativeWarlock commented on GitHub (May 24, 2023): Thanks for the link to tiers. I have obviously overlooked that feature in your docs. Yes sadly, if users can't connect to nfty topics through the credentials they get from our system, i.e. after they register & subscribe to our product(s), the business case is not functional and we cannot move on. Let me know if you need additional infos.
Author
Owner

@CreativeWarlock commented on GitHub (May 24, 2023):

The user management in the Android app is subpar. I will work on that in the near future. For now, you can add an existing (!) user like you said, but you cannot create a user in the Android app.

Wait a sec.. it IS possible to connect with an existing user to the server?
That would be perfectly enough then. Question is: How?

image

That "Benutzer hinzufügen" (Add user) seems to be no link, so idk how to configure the app to use an existing user (that gets created & configured from my backend service).

I'm most likely missing something - so any hint would be much appreciated! :)

<!-- gh-comment-id:1561911515 --> @CreativeWarlock commented on GitHub (May 24, 2023): > The user management in the Android app is subpar. I will work on that in the near future. For now, you can add an existing (!) user like you said, but you cannot create a user in the Android app. Wait a sec.. it IS possible to connect with an existing user to the server? That would be perfectly enough then. Question is: How? ![image](https://github.com/binwiederhier/ntfy/assets/4894323/da88c14f-bea2-4921-a03c-22f4dc9840d1) That "Benutzer hinzufügen" (Add user) seems to be no link, so idk how to configure the app to use an existing user (that gets created & configured from my backend service). I'm most likely missing something - so any hint would be much appreciated! :)
Author
Owner

@binwiederhier commented on GitHub (May 25, 2023):

My apologies for the confusion regarding this "user add" feature.

You are in the correct screen. To add an existing user in the Android app, you have to go to Settings -> Manage users -> Add new user" (Neuen Benutzer hinzufügen). And then add the user for whatever your server is.

The likely reason it didn't work for you above is that you have to prefix the server with "http://" or "https://" in the "Server URL field, e.g. "https://ntfy.example.com" is correct, but "ntfy.example.com" will not work. As I said, the UX is quite annoying. 😬

image

<!-- gh-comment-id:1562169200 --> @binwiederhier commented on GitHub (May 25, 2023): My apologies for the confusion regarding this "user add" feature. You are in the correct screen. To add an existing user in the Android app, you have to go to Settings -> Manage users -> Add new user" (Neuen Benutzer hinzufügen). And then add the user for whatever your server is. The likely reason it didn't work for you above is that you have to prefix the server with "http://" or "https://" in the "Server URL field, e.g. "https://ntfy.example.com" is correct, but "ntfy.example.com" will not work. As I said, the UX is quite annoying. :grimacing: ![image](https://github.com/binwiederhier/ntfy/assets/664597/ecb99660-7603-4fb4-a0d6-b3bf72767d3d)
Author
Owner

@CreativeWarlock commented on GitHub (May 25, 2023):

Ah yes, sorry that I didn't try adding the protocol myself.

(Btw. I wrote you on Discord because messages don't come through. It's maybe my server settings or something else is missing between NTFY server and the NTFY app.)

<!-- gh-comment-id:1562705698 --> @CreativeWarlock commented on GitHub (May 25, 2023): Ah yes, sorry that I didn't try adding the protocol myself. (Btw. I wrote you on Discord because messages don't come through. It's maybe my server settings or something else is missing between NTFY server and the NTFY app.)
Author
Owner

@CreativeWarlock commented on GitHub (May 27, 2023):

Ok, your feature seems to work fine: I just sent a message to a topic, the NTFY server returned a success response and the message arrives in the app. However, with a delay of 30 minutes.

<!-- gh-comment-id:1565355351 --> @CreativeWarlock commented on GitHub (May 27, 2023): Ok, your feature seems to work fine: I just sent a message to a topic, the NTFY server returned a success response and the message arrives in the app. However, with a delay of 30 minutes.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/ntfy#526
No description provided.