Change:
Before the client can upload its report to the leader, it must know the public key of each of the aggregators.  These are retrieved from <del> each aggregator by sending a request to "[aggregator]/key_config", where "[aggregator]" is the aggregator's endpoint URL, obtained from the task parameters.  The aggregator responds to well-formed requests with status 200 and an "HpkeConfig" value: </del> <ins> each aggregator by sending a request to "[aggregator]/hpke_config?task_id=[task-id]", where "[aggregator]" is the aggregator's endpoint URL, obtained from the task parameters, and "[task-id]" is the task ID obtained from the task parameters, encoded in Base 64 with URL and filename safe alphabet with no padding, as specified in sections 5 and 3.2 of RFC4648.  If the aggregator does not recognize the task ID, then it responds with HTTP error status 404 Not Found and an error of type "unrecognizedTask".  The aggregator responds to well-formed requests with status 200 and an "HpkeConfig" value: [TODO: Allow aggregators to return HTTP 403 Forbidden in deployments that use authentication to avoid leaking information about which tasks exist.] </ins> [OPEN ISSUE: Decide whether to expand the width of the id, or support multiple cipher suites (a la OHTTP/ECH).] <del> The client MUST abort if any of the following happen for any "key_config" request: </del> <ins> The client MUST abort if any of the following happen for any HPKE config request: </ins> the client and aggregator failed to establish a secure, aggregator-authenticated channel; <del> the GET request failed or didn't return a valid key config; or </del> <ins> the GET request failed or didn't return a valid HPKE configuration; or </ins> <del> the key config specifies a KEM, KDF, or AEAD algorithm the client doesn't recognize. </del> <ins> the HPKE configuration specifies a KEM, KDF, or AEAD algorithm the client doesn't recognize. </ins> Aggregators SHOULD use HTTP caching to permit client-side caching of this resource RFC5861.  Aggregators SHOULD favor long cache lifetimes

Discussion:
When obtaining an aggregator's HPKE configuration, clients now specify the task ID they are interested in. Aggregators are not required to use a distinct HPKE configuration for each task, but now it's possible for them to do so. The endpoint is also renamed to from for clarity.
Aggregators are expected to have an endpoint from which they vend the HPKE public key and parameters so that clients may encrypt the contents of reports. There are no parameters to the request, so there is no opportunity for an aggregator to provide a different config based on the task in play. Now, the endpoint is relative to the aggregator's base endpoint URL, obtained from the per-task parameters, so an aggregator could simply provide base URLs that have the task ID in their path and use different key material on that basis. But all the other protocol messages like or explicitly include task ID, suggesting that aggregators are supposed to be able to service multiple tasks from a single endpoint. Something feels inconsistent there.
I think we could change the endpoint to . We would probably have to redefine task ID to make sure it's safe to use in URL construction (URL encoded byte strings look terrible).
Good catch, and I like your suggested fix.
I could swear we talked about this a long time ago. Anyway, I am putting this in for now because I don't think any progress is blocked on it, and it strikes me as the kind of thing that we will go back and forth on a lot during the IETF process.
I think the real issue here is that when handling a message, collectors, leaders and helpers need to know which task it pertains to in order to select the appropriate HPKE context (among other parameters). We should decide whether the task ID appears in protocol messages or whether it can be inferred from the endpoint and be consistent about it. Retitling accordingly.
nit: IMO it would be more consistent to put the task ID in a request into the request body -- all other request/response pairs include the task ID in the request body, rather than including it in the request path. (There's no technical downside to putting the task ID in the path, this is only a "consistency" concern.)
I think the issue with putting the task ID for a /hpke_config request in the body is that we have to switch from GET to POST, and lose cache-ability.

That's a good point -- it's still pretty unsatisfying that this one request/response pair will have to be structured in this way, while all other request/response pairs (Upload, Aggregate, AggregateShare, Collect) use a different structuring. However, given that some clients will literally be web browsers, where HTTP caching is "free", I think it's worthwhile to keep HTTP caching semantics working.
(FWIW, this would be a great place to use which is "GET but with a body", except that (a) it's still not standardized and (b) even if it were, it's still new enough that many HTTP libraries won't have good support for it.)
One final thought for now: if we do stick to a GET request, we should consider putting the task ID for an request in a query parameter (e.g. ) rather than including it as part of the request path. Otherwise, every other request type will duplicate the task ID in both the request path & the request body.
The endpoint has been changed to take the task ID as a query parameter, and all other messages include a task ID in the body. Should we close this out?
Nice