GETTING STARTED

Your first authenticated call

client_credentials

Two values from your account, one token request, one API call. There is nothing else to configure — no audience, no scope, no project id.

What you need

Exactly two values, both from the management portal under API credentials: a client id and a client secret. The secret is shown once, when you create the credential.

Everything else — the token endpoint, the issuer, and the base URL of each API — is fixed for your environment and listed below. You do not need to look any of it up, and you should not be asked to invent it.

Building with an AI assistant?

Connect it to the Mesh MCP server and have it call get_connection_details. That returns the token endpoint, the issuer and every API base URL already resolved for your environment, so the only thing you paste in is the client id and secret. If an assistant asks you for a token URL or a scope, it has not called that tool.

Get a token

Exchange the credential at the token endpoint using the OAuth 2.0 client_credentials grant. The request is form-encoded and has three fields.

curl -X POST https://auth.mesh.locate2u.app/realms/mesh/protocol/openid-connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=$MESH_CLIENT_ID" \
  -d "client_secret=$MESH_CLIENT_SECRET"

The response contains access_token and expires_in. Cache the token until shortly before it expires rather than requesting one per call.

Send no scope

There is no scope parameter to add, and adding one makes the whole request fail with invalid_scope. The audience every Mesh API checks is attached automatically, and the permissions you granted the credential travel inside the token as claims.

A Mesh permission is not an OAuth scope. If your credential grants maps.read, that is already in the token — sending scope=maps.read asks the identity provider for something it has never heard of, and the request is rejected before any Mesh service sees it.

Call an API

Send the token as a bearer token. Every Mesh API accepts the same token.

curl https://stops-api.mesh.locate2u.app/api/stops \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Each API lives on its own hostname, following one pattern: <module>-api.<environment>. The management API is management-api.<environment>.

Environments

Hostnames differ only by the environment label. Production has no label.

EnvironmentBaseToken endpoint
Productionmesh.locate2u.appauth.mesh.locate2u.app/realms/mesh/protocol/openid-connect/token
Testtest-mesh.locate2u.appauth.test-mesh.locate2u.app/realms/mesh/protocol/openid-connect/token
The realm segment is not optional

The issuer is https://auth.<environment>/realms/mesh, and every Mesh service compares it as an exact string. A valid token from the right server with the wrong issuer path is refused as though its signature were broken — which reads like a key problem and is not one.

When it fails

Three failures cover almost everything.

  • invalid_scope from the token endpoint — you sent a scope. Remove it.
  • 401 from a Mesh API — the token is missing, expired, or came from a different environment than the API you are calling. Check the environment label matches on both hostnames.
  • 403 from a Mesh API — the token is valid, but the credential lacks the permission that endpoint needs, or the account does not have the module that endpoint belongs to. The response body names which.
A missing module is not something you can fix in code

If a 403 says the module is not enabled, no amount of permission-granting will change it. Which modules an account has is agreed in its Locate2u agreement and applied by Locate2u — contact your account manager.