\n and \n\n both result in the expected output. However, if I have something like \n\n\n or more, I get the same result as \n\n. Using literal newlines results in the same thing.
From what I can understand of the code (with the help of Google AI Gemini), the current behavior of org-roam-capture collapses multiple consecutive newlines into a single one, except when they occur at the very end of the template.
The primary function responsible for this behavior is org-roam-capture--fill-template:
(replace-regexp-in-string "[\n]*\\'" "" ...): This line is the culprit for collapsing multiple newlines. It uses a regular expression to find any sequence of zero or more newlines followed by a single quote and replaces it with an empty string. This effectively removes any extra newlines that might have been present within the template, except for those at the very end.
(when (and ensure-newline ...)): This part ensures that if the ensure-newline argument is provided and there’s no trailing whitespace in the original template, a single newline is appended. If the original template had trailing whitespace (spaces, tabs, or newlines), that whitespace is preserved in template-whitespace-content and appended back to the final template.
In other words:
In-between whitespace (multiple newlines within the template body): These newlines are collapsed into a single newline due to the replace-regexp-in-string operation.
At-the-end whitespace (newlines at the very end of the template): These newlines are preserved due to the ensure-newline logic, which appends a newline only if there’s no existing trailing whitespace. This specific code was changed after the following Bug report. Related: Multiple blank lines in head of capture template