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
| Header | Value |
|---|---|
Content-Type | application/x-www-form-urlencoded |
Body parameters (form-urlencoded)
grant_typeStringrequiredMust be
client_credentials.client_idStringrequiredYour merchant client ID.
client_secretStringrequiredYour 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 response | Cause |
|---|---|
{ "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. |