Skip to main content

Two tokens, one integration

You are one customer with one contract. But your integration has up to two moving parts, and they authenticate differently — which is the single thing new integrators most often get wrong.

Your backendYour app
Credentialmachine token (client_credentials)the wallet holder's own token
Obtained byyour server, from your client id and secretthe person signing in via /auth/login, or your backend via /auth/delegated-token
Acts asyour tenantthat one person

There is one API reference covering both, and every operation states the token it takes at the top of its description. You never have to work out which section an operation lives in — most people arrive from search anyway.

If you want a spec scoped to one half — to import into Postman, or to generate a client — download wallet-backend-api.yaml or wallet-app-api.yaml instead of the full wallet-api.yaml.

If your users log in to you, not to us

The table above assumes the wallet holder signs in through us. Most integrators with an existing product do not want that — their users already have an account, and a second login is a worse experience and a support burden.

For that case your backend mints the user token itself:

POST /auth/delegated-token
Authorization: Bearer <your machine token>

{ "externalUserRef": "drv-7001" }

The result is an ordinary user token — /me/* works exactly as it would after /auth/login — with two extra claims that record where the authentication came from:

{ "amr": ["tenant_delegated"], "act": { "sub": "cli_8a91e0e8..." } }

amr says you authenticated this person; act names the client that asserted them. Neither changes what the token can do. Both exist so that a dispute, years later, can tell "GoHubPay verified a password" from "the tenant vouched for their user" — from the token alone.

There is no refresh token: mint another when it expires. A refresh would let the session outlive your ability to re-assert the user, which is authority you have not given us.

See the walkthrough for the full sequence.

Why a machine token cannot do everything

Operations under /me/* resolve the caller from the token and act only as that person. There is no userId parameter to pass, which is exactly what makes them safe to ship inside a mobile app — and also why a machine token has nothing to resolve to. It is rejected with 401: "Authenticated principal is not a wallet user."

This is not a permission you can be granted. It is structural.

The one case that behaves differently by caller

GET /transactions and the two transaction reads are callable with either token, and return different things:

  • with your machine token — transactions across your whole tenant
  • with a user token — only that person's own activity

The narrowing happens on our side; you cannot widen it by passing different parameters. If you proxy this endpoint from your app, be deliberate about which token you forward.

If your backend instructs everything

If you never mint a delegated token, your users hold no session and no operation marked User token applies to you — roughly a third of the reference is not part of your integration, including the whole /me/* surface.

That is a choice, not a limit. Minting a delegated token gets any registered user an ordinary session and the full surface with it. See How the integration works.