@kwdef constructor not available outside of module

Thank you for your help! Yes, wrapping everything in an escape call works, although it is of course a bit ugly (and throws off Emacs’ indentation).

A colleague pointed out that with Julia 1.11, an issue with @kwdef has been fixed so that it is now easier to use in macros: Support escape expressions in @kwdef by ettersi · Pull Request #53230 · JuliaLang/julia · GitHub

This allows the following code:

macro species(name, body)
    quote
        @kwdef mutable struct $(esc(name))
            const id::Int
            $(body.args...)
        end
        $(esc(name))(id) = $(esc(name))(id=id)
    end
end

The change to the first version is simply that the struct name is also escaped, which I personally find cleaner.