JWT was meant to be stateless, if it's not, then it's just a layer of unnecessary complexity with potential security and implementation flaws.
The web server gets a token from the API server, then prepares a few JSON messages that the web client will send asynchronously with JS. Since each message content is signed, the web client can't tamper with what is sent to the API. JWT was perfect for this 3-tiers messaging.
If you're talking about something bespoke then it probably doesn't.
Isn't that JWT?
It's possible to add additional information in a JWT. And of course it's complexity that adds additional attack surface, but at least there is some kind of standardization around it.
Basically, Clients all used NTLM to talk to the main site, but the main site would use JWT to pass the authenticated user info to the other services being called. The signature ensured that you couldn't spoof, short of being an authorized user that could get an impersonate token for calling the APIs.
But the nice thing was it meant we didn't really have to hit the DB at all in any of this, and it was way cheaper to implement than an API gateway.
If you work mainly in Java for example, you'll more often see JSESSIONID which are random string identifiers, referring to a database containing active tokens and user profiles.
However if you work in Python, you'll more often see objects. Typically something like a user identifier + creation date + random bits, that is encrypted with a symmetric key. It's usually encrypted, not signed, so yet another thing than signed tokens and random cookies.