Authentication

PayLater uses OAuth 2.0 client credentials. Exchange your client_id and client_secret for a short-lived bearer token, then send it as Authorization: Bearer <ACCESS_TOKEN> on every API call.

Token lifecycle

The access token lives for expires_in seconds (300 = 5 minutes). Cache it, reuse it until just before expiry, then re-authenticate. A single token covers all your outlets — pass outlet_id per request.

POST /auth/realms/api/protocol/openid-connect/token
Sandbox https://connect.uat.paylaterapp.com/auth/realms/api/protocol/openid-connect/token
Production https://connect.paylaterapp.com/auth/realms/api/protocol/openid-connect/token

Request

Headers

HeaderValue
Content-Typeapplication/x-www-form-urlencoded
Body parameters (form-urlencoded)
grant_typeStringrequired
Must be client_credentials.
client_idStringrequired
Your merchant client ID.
client_secretStringrequired
Your merchant client secret.

Example request

curl --location 'https://connect.uat.paylaterapp.com/auth/realms/api/protocol/openid-connect/token' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode 'client_id=<CLIENT_ID>' \
  --data-urlencode 'client_secret=<CLIENT_SECRET>'
const body = new URLSearchParams({
  grant_type: 'client_credentials',
  client_id: '<CLIENT_ID>',
  client_secret: '<CLIENT_SECRET>',
});

const res = await fetch(
  'https://connect.uat.paylaterapp.com/auth/realms/api/protocol/openid-connect/token',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body,
  },
);

const { access_token } = await res.json();
import requests

resp = requests.post(
    "https://connect.uat.paylaterapp.com/auth/realms/api/protocol/openid-connect/token",
    headers={
        "Content-Type": "application/x-www-form-urlencoded",
    },
    data={
        "grant_type": "client_credentials",
        "client_id": "<CLIENT_ID>",
        "client_secret": "<CLIENT_SECRET>",
    },
)

access_token = resp.json()["access_token"]
$ch = curl_init('https://connect.uat.paylaterapp.com/auth/realms/api/protocol/openid-connect/token');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/x-www-form-urlencoded',
    ],
    CURLOPT_POSTFIELDS => http_build_query([
        'grant_type' => 'client_credentials',
        'client_id' => '<CLIENT_ID>',
        'client_secret' => '<CLIENT_SECRET>',
    ]),
]);

$response = json_decode(curl_exec($ch), true);
$accessToken = $response['access_token'];

Response

A 200 OK returns the bearer token and its lifetimes.

{
  "access_token": "ey***********Dw",
  "expires_in": 300,
  "refresh_expires_in": 1800,
  "refresh_token": "ey**********kQ",
  "token_type": "Bearer",
  "scope": "profile email"
}

Errors

Example responseCause
{ "error": "invalid_client", "error_description": "Invalid client or Invalid client credentials" }Wrong client_id / client_secret.
{ "error": "unsupported_grant_type", "error_description": "Unsupported grant_type" }grant_type must be client_credentials.