Querying account data of a user

Your question

I have e.g. the user ‘ralf’ from the domain ‘example.org’, Now I try to get his account-data via JMAP with the following request:

{
   "methodRequests": [
      ["x:Domain/query", {
		"filter": {
           "name": "example.org"
        } }, "a"],
      ["x:Account/query", {
		"filter": {
			"name": "ralf",
			"#domainId" => {"name": "x:Domain/query", "path": "/ids", "resultOf": "a"},
        } }, "b"],
   ], 
   "using": ["urn:ietf:params:jmap:core", "urn:stalwart:jmap"]
}

But instead of the existing account, I got the following error:

{
   "methodResponses":[
      ["x:Domain\/query": {
         "accountId":"b",
         "queryState":"n",
         "canCalculateChanges":true,
         "position":0,
         "ids":["b"]
      }, "a"],
      ["error": {
         "type":"unsupportedFilter",
         "description":"#domainId"
      },"b"]],
   "sessionState":"a5401889"
}

What is the problem here?

Does “filter” not allow JMAP references?

Ralf

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

Now I have a clear picture. Let me check how the filter’s domainId condition is actually implemented vs. what the PrincipalFilter enum handles:

Looking at the source code, PrincipalFilter only handles: accountIds, email, name, text, type, and timeZone. The domainId key falls into the _T(String) catch-all, which then triggers the unsupportedFilter error in the query handler.

There are two separate issues in your request:

1. domainId is not supported as a filter in x:Account/query (despite being listed in the docs)

The docs show domainId as a valid filter condition for x:Account/query, but looking at the server source (crates/jmap-proto/src/object/principal.rs), the deserialization for PrincipalFilter only handles accountIds, email, name, text, type, and timeZone. Any unrecognized key – including domainId – falls into the _T catch-all, which then triggers UnsupportedFilter. This looks like a bug where the docs describe a filter that the server doesn’t yet implement.

2. JMAP back-references (#field) in filter are not supported here

Even if domainId were handled, the #domainId back-reference syntax (RFC 8620 Section 9 result references) is only supported for certain argument types. The x:Account/query filter parser does not implement result-reference substitution for filter conditions – it would silently ignore or fail the reference. You also have a syntax error: the arrow (=>) is PHP syntax, not JSON; JSON requires :).

Workaround

Do it in two separate requests: first call x:Domain/query to get the domain ID, then use that literal ID in a second call to x:Account/query with "domainId": "<id>" – once/if that filter is actually wired up. In the meantime, filtering by name ("name": "ralf") and then checking the returned account’s domainId field client-side is the practical approach.

The apparent gap between the documented domainId filter and the server implementation is worth flagging – a human community or staff member may be able to confirm whether it is planned or already merged in a newer build.

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

Ok, the => is just a mistake in the manual translation of my PHP code, not really a problem.

Do I need to report the not existing, but documented, and probably really necessary, domainId filter as bug, or can this post simply be changed to one?

Not generally supporting JMAP references, is a minor problem, thought it will create support questions all the time …

An other useful filter would be @Type: User, to not return matching groups.

Ralf

The ause of the unsupportedFilter is that result references (the #-prefixed resultOf/path back-references) aren’t resolved inside a filter; they only apply to specific argument slots like #ids, never to arbitrary filter conditions, so #domainId is rejected as an unknown filter key.

domainId itself is a valid filter property on accounts, but the value has to be a literal id string, not a reference. So this is two steps: query the domain by name to get its id, read that id from the response, then issue the account query with the literal value, for example filter { “name”: “ralf”, “domainId”: “” }. You can batch both methods in one request, but the second can’t consume the first’s result inside its filter. The account filter properties available are name, domainId, type, text, memberGroupIds and memberTenantId.