OK, I seem to have found the solution, after many days of trial and error.
One of my colleagues found me this forum post: https://serverfault.com/questions/533858/how-to-configure-a-2821-isr-to-an-asterisk-pbx-on-a-pri-line
The problem was my SIP trunk between FreePBX and the cisco gateway. The context set in the “Outgoing” tab should be set to “from-trunk”, whereas in my previous post I had it set to “from-internal”. Proof that you can’t always blindly copy-paste stuff from the internet and hope it works without understanding what it does.
context=from-trunk
host=10.3.1.20
type=friend
qualify=yes
dtmfmode=rfc2833
disallow=all
allow=ulaw
nat=no
insecure=very
Now everything works perfectly!