Remove JMAP pushsubcriptions without waiting for expiry

Your question

I am trying to test a push service for JMAP email client (bulwark) and I have stumbled on a overquota error for PushSubscriptions. No matter if i up the limit or not, it will not allow for anymore push subscriptions. The error i get is

PushSubscription/set create failed type “overQuota” Description “There are too many subscriptions, please delete some before adding a new one”

From the documentation, the pushsubscriptions are created per user, and testing a new blank server, I have been able to successfully set up push notifications. Is there a way to remove all current PushSubscriptions so I can create new ones with the current server?

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

Good - I have enough context to give a solid answer. The source code confirms:

  1. Standard JMAP PushSubscription/set with destroy works to delete subscriptions.
  2. When doing a PushSubscription/get with no IDs (i.e. "ids": null), you get all current subscriptions back, which you can then destroy.
  3. The quota is checked against non-expired subscriptions (expired ones are pruned automatically when you do a get).

Here is how to resolve this:

Step 1 – list all your current subscriptions (as the affected user):

curl -X POST https://mail.example.com/jmap \
  -H 'Authorization: Bearer $TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:push"],
    "methodCalls": [
      ["PushSubscription/get", { "ids": null }, "c1"]
    ]
  }'

This returns all subscriptions with their IDs. It also triggers a purge of any already-expired entries, which may free up quota on its own.

Step 2 – destroy the ones you no longer need:

Take the id values from the response and pass them to PushSubscription/set:

curl -X POST https://mail.example.com/jmap \
  -H 'Authorization: Bearer $TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:push"],
    "methodCalls": [
      ["PushSubscription/set", {
        "destroy": ["<id1>", "<id2>"]
      }, "c1"]
    ]
  }'

A few things to note from the source code:

  • The $TOKEN must belong to the same user account whose subscriptions you want to manage – PushSubscription operations are scoped per user (no accountId parameter).
  • Subscriptions automatically expire after at most 7 days, so any stale ones from testing will clean themselves up on the next PushSubscription/get call.
  • If you want to raise the per-user limit, that is controlled by the MaxPushSubscriptions object quota, which can be adjusted via the WebUI or JMAP API.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

hmm assuming the $token is an API key with the correct permissions, i get an error 400…a little stuck here.

Is an API key OK as the $TOKEN or should I be using something else?

The error i get is…

{“type”:“urn:ietf:params:jmap:error:notRequest”,“status”:400,“detail”:“{\n \“using\”: [\“urn:ietf:params:jmap:core\”,\“urn:ietf:params:jmap:push\”],\n \“methodCalls\”: [\n [\“PushSubscription/get\”, { \“ids\”: null }, \“c1\”]\n ]\n }”}

Figured it out.

"urn:ietf:params:jmap:push"

Does not exist and threw the error. removing it from the CURL command for both returned the push subscription IDs and deleted the subscriptions