Skip to content

Authentication

UniPay uses OAuth 2.0 client credentials — machine to machine, with no user involved. Your server exchanges its client_id and client_secret for an access token, then sends the token on every API call.

POST /connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&scope=unipay:bills
{
"access_token": "eyJhbGciOiJSUzI1NiIs…",
"token_type": "Bearer",
"expires_in": 1799
}
  • Send the credentials in the form body, as above, or as an Authorization: Basic base64(client_id:client_secret) header. Either works; pick one.
  • URL-encode the values — a secret containing +, & or # breaks otherwise.
  • Tokens last 30 minutes. No refresh token is issued or needed: ask for a new token instead.
  • Every environment publishes its OAuth settings at /.well-known/openid-configuration.

Then send the token on every call:

POST /api/app/payment-processor/lookup
Authorization: Bearer eyJhbGciOiJSUzI1NiIs…

Your organisation is identified from the token. There is no tenant header to send.

Request a token once and reuse it until shortly before it expires. Requesting one per call is slow, and the token endpoint is rate-limited to 10 requests a minute.

  • Renew when about 60 seconds remain, so requests already in flight don’t carry an expired token.
  • Hold one token per process and guard the renewal with a lock, so concurrent requests don’t all renew at once.
  • If a call gets 401, drop the cached token, get a new one and retry once. A second 401 means the credentials are wrong or revoked — don’t loop.
  • Keep the token in memory, never on disk, and never log it.
Terminal window
# Tokens last 30 minutes: reuse one for every call in that window.
TOKEN=$(curl -sS --fail-with-body -X POST "$BASE_URL/connect/token" \
--data-urlencode "grant_type=client_credentials" \
--data-urlencode "client_id=$UNIPAY_CLIENT_ID" \
--data-urlencode "client_secret=$UNIPAY_CLIENT_SECRET" \
--data-urlencode "scope=unipay:bills" | jq -r .access_token)

Ask only for the scopes your integration needs, as a space-separated list in scope.

Scope Grants Who needs it
unipay:bills Processors: look up and pay bills. Providers: publish bills. Everyone
unipay:payments Read the payments and reversals made against your bills. Needs unipay:bills too. Service providers
unipay:directory List the service providers you can pay. Payment processors, optional
unipay:directory.settlement Adds each provider’s settlement accounts to the directory, account numbers masked. Processors that settle directly
unipay:directory.settlement.reveal Reveals a settlement account’s full number. Every reveal is audited. Only processors that initiate transfers

A call without the scope it needs gets 403 Forbidden with an empty body; the WWW-Authenticate response header says error="insufficient_access".

Credentials only work on their own side of UniPay: payment-processor credentials can’t call service-provider endpoints, and the reverse. Such a call gets 403 with an error object whose code is Authorization:PartnerTypeMismatch.

The token endpoint answers 401 (bad credentials) or 400 (anything else) with a standard OAuth error body — error, error_description and error_uri:

error Meaning
invalid_client Unknown client_id, or wrong client_secret. Check for encoding problems in the secret.
invalid_scope A scope name UniPay doesn’t recognise.
invalid_request A real scope your credential isn’t allowed. Ask for it to be added, or drop it.
access_denied The credential has been disabled or revoked.
unsupported_grant_type grant_type isn’t client_credentials.
  1. Keep client_secret in a secrets vault. Never commit it or ship it to a phone, browser or POS device.
  2. Use separate credentials for test and production, and for each system that calls UniPay.
  3. Rotate secrets at least every 90 days — see Managing credentials.
  4. Treat access tokens like passwords: keep them in memory, never log them or echo them in errors.
  5. Call UniPay only over HTTPS, and don’t disable certificate validation.

UniPay API v1 · build e1eb5c49 · Powered by Axis Solutions