curl -X POST "${BASE_URL}/oauth/token" \
-H "Accept: application/json" \
-H "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}"import json
import os
import urllib.parse
import urllib.request
base_url = os.environ["BASE_URL"].rstrip("/")
url = base_url + "/oauth/token"
headers = {"Accept": "application/json"}
params = {
"grant_type": "client_credentials",
"client_id": os.environ["CLIENT_ID"],
"client_secret": os.environ["CLIENT_SECRET"],
}
body = urllib.parse.urlencode(params).encode("utf-8")
headers["Content-Type"] = "application/x-www-form-urlencoded"
request = urllib.request.Request(url, data=body, headers=headers, method="POST")
with urllib.request.urlopen(request, timeout=30) as response:
print(response.read().decode("utf-8"))
const env = (name) => {
const value = process.env[name];
if (!value) throw new Error(`Missing environment variable: ${name}`);
return value;
};
const baseUrl = env("BASE_URL").replace(/\/+$/, "");
const url = new URL(baseUrl + "/oauth/token");
const headers = { Accept: "application/json" };
const params = new URLSearchParams({
"grant_type": "client_credentials",
"client_id": env("CLIENT_ID"),
"client_secret": env("CLIENT_SECRET"),
});
const body = params;
headers["Content-Type"] = "application/x-www-form-urlencoded";
const response = await fetch(url, {
method: "POST",
headers,
body,
signal: AbortSignal.timeout(30_000),
});
console.log(await response.text());
<?php
$env = static function (string $name): string {
$value = getenv($name);
if ($value === false || $value === '') {
throw new RuntimeException("Missing environment variable: " . $name);
}
return $value;
};
$baseUrl = rtrim($env('BASE_URL'), '/');
$url = $baseUrl . "/oauth/token";
$headers = ['Accept: application/json'];
$params = [
"grant_type" => "client_credentials",
"client_id" => $env("CLIENT_ID"),
"client_secret" => $env("CLIENT_SECRET"),
];
$body = http_build_query($params, '', '&', PHP_QUERY_RFC3986);
$headers[] = 'Content-Type: application/x-www-form-urlencoded';
$curl = curl_init($url);
curl_setopt_array($curl, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($curl);
if ($response === false) {
$error = curl_error($curl);
curl_close($curl);
throw new RuntimeException($error);
}
curl_close($curl);
echo $response, PHP_EOL;
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
)
func mustEnv(name string) string {
value := os.Getenv(name)
if value == "" { panic("Missing environment variable: " + name) }
return value
}
func main() {
baseURL := strings.TrimRight(mustEnv("BASE_URL"), "/")
endpoint := baseURL + "/oauth/token"
params := url.Values{}
params.Set("grant_type", "client_credentials")
params.Set("client_id", mustEnv("CLIENT_ID"))
params.Set("client_secret", mustEnv("CLIENT_SECRET"))
body := strings.NewReader(params.Encode())
req, err := http.NewRequest("POST", endpoint, body)
if err != nil { panic(err) }
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{Timeout: 30 * time.Second}
response, err := client.Do(req)
if err != nil { panic(err) }
defer response.Body.Close()
result, err := io.ReadAll(response.Body)
if err != nil { panic(err) }
fmt.Println(string(result))
}
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
public class Example {
static String requireEnv(String name) {
String value = System.getenv(name);
if (value == null || value.isEmpty()) {
throw new IllegalArgumentException("Missing environment variable: " + name);
}
return value;
}
public static void main(String[] args) throws Exception {
String baseUrl = requireEnv("BASE_URL").replaceAll("/+$", "");
String endpoint = baseUrl + "/oauth/token";
Map<String, String> params = new LinkedHashMap<>();
params.put("grant_type", "client_credentials");
params.put("client_id", requireEnv("CLIENT_ID"));
params.put("client_secret", requireEnv("CLIENT_SECRET"));
String encoded = params.entrySet().stream()
.map(e -> URLEncoder.encode(e.getKey(), StandardCharsets.UTF_8) + "="
+ URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&"));
HttpRequest.Builder builder = HttpRequest.newBuilder(URI.create(endpoint))
.timeout(Duration.ofSeconds(30))
.header("Accept", "application/json");
builder.header("Content-Type", "application/x-www-form-urlencoded");
HttpRequest request = builder.method("POST", HttpRequest.BodyPublishers.ofString(encoded, StandardCharsets.UTF_8)).build();
HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
System.out.println(response.body());
}
}
require "net/http"
require "uri"
base_url = ENV.fetch("BASE_URL").sub(%r{/+$}, "")
uri = URI(base_url + "/oauth/token")
params = {
"grant_type" => "client_credentials",
"client_id" => ENV.fetch("CLIENT_ID"),
"client_secret" => ENV.fetch("CLIENT_SECRET"),
}
request = Net::HTTP::Post.new(uri)
request["Accept"] = "application/json"
request.set_form_data(params)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == "https"
http.open_timeout = 30
http.read_timeout = 30
puts http.request(request).body
{
"token_type": "Bearer",
"expires_in": 7200,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiI4YTZiZjJkOS00N2MxLTRlODUtOTJiMy02ZDFhN2MwODRmNTIiLCJqdGkiOiJhZGRiNDgzNDkxMGM2MThjZmJiNGRhMTJhZDMwNWEwNGU3ZmZiNWUzMTE3YjI3YmQwNTVlMjk5ZTgzOTU1ZTkyIiwiaWF0IjoxODkzNDUyNDAwLCJuYmYiOjE4OTM0NTI0MDAsImV4cCI6MTg5MzQ1OTYwMCwic3ViIjoiOGE2YmYyZDktNDdjMS00ZTg1LTkyYjMtNmQxYTdjMDg0ZjUyIiwic2NvcGVzIjpbXX0.dLrN3edou1KGfN5a2kbfDRtGA-iM4meEfwlMTPc1UHv1wGrv70ue8Mb3UU0TYxbZHxK-kHHUVH9aWwItbsyYBkou3Sgr5NAwejvuxSOmQZHk4dT-Uw982pelDqBrBmJV5qZ5qY7qeGDc0Fz_0ymre50BLOd2d3MsLj-INdwjNNIF4fr2AG9M6mDK8pEqGMNMqT20GGVEQ1YJZGwpy-XiPp-6sVS3qOjyineqTK_hN3N0QXykoEM4iFGK_wQdc681B8tfXJRpcQksNt4A1pM2G5nUdnBC-ahByGYUaij4JAkxR_LpnXoR1xQbd-j4wj8cG78WAPdJTzZcIFPRU0Gmjw"
}{
"error": "invalid_request",
"error_description": "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed.",
"hint": "Check the `grant_type` parameter"
}{
"error": "invalid_client",
"error_description": "Client authentication failed"
}{
"message": "Too Many Attempts."
}Obtain an OAuth access token
Obtains an access token using OAuth2 client_credentials. Client credentials must be approved, enabled, not revoked and not expired. If an IP allowlist is configured, business requests must originate from an approved IP. Use the Client Secret and access_token only on the merchant server. Request a new token when it expires; no refresh_token is provided. Token requests are limited to 60 per minute; exceeding the limit returns HTTP 429.
curl -X POST "${BASE_URL}/oauth/token" \
-H "Accept: application/json" \
-H "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}"import json
import os
import urllib.parse
import urllib.request
base_url = os.environ["BASE_URL"].rstrip("/")
url = base_url + "/oauth/token"
headers = {"Accept": "application/json"}
params = {
"grant_type": "client_credentials",
"client_id": os.environ["CLIENT_ID"],
"client_secret": os.environ["CLIENT_SECRET"],
}
body = urllib.parse.urlencode(params).encode("utf-8")
headers["Content-Type"] = "application/x-www-form-urlencoded"
request = urllib.request.Request(url, data=body, headers=headers, method="POST")
with urllib.request.urlopen(request, timeout=30) as response:
print(response.read().decode("utf-8"))
const env = (name) => {
const value = process.env[name];
if (!value) throw new Error(`Missing environment variable: ${name}`);
return value;
};
const baseUrl = env("BASE_URL").replace(/\/+$/, "");
const url = new URL(baseUrl + "/oauth/token");
const headers = { Accept: "application/json" };
const params = new URLSearchParams({
"grant_type": "client_credentials",
"client_id": env("CLIENT_ID"),
"client_secret": env("CLIENT_SECRET"),
});
const body = params;
headers["Content-Type"] = "application/x-www-form-urlencoded";
const response = await fetch(url, {
method: "POST",
headers,
body,
signal: AbortSignal.timeout(30_000),
});
console.log(await response.text());
<?php
$env = static function (string $name): string {
$value = getenv($name);
if ($value === false || $value === '') {
throw new RuntimeException("Missing environment variable: " . $name);
}
return $value;
};
$baseUrl = rtrim($env('BASE_URL'), '/');
$url = $baseUrl . "/oauth/token";
$headers = ['Accept: application/json'];
$params = [
"grant_type" => "client_credentials",
"client_id" => $env("CLIENT_ID"),
"client_secret" => $env("CLIENT_SECRET"),
];
$body = http_build_query($params, '', '&', PHP_QUERY_RFC3986);
$headers[] = 'Content-Type: application/x-www-form-urlencoded';
$curl = curl_init($url);
curl_setopt_array($curl, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($curl);
if ($response === false) {
$error = curl_error($curl);
curl_close($curl);
throw new RuntimeException($error);
}
curl_close($curl);
echo $response, PHP_EOL;
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
)
func mustEnv(name string) string {
value := os.Getenv(name)
if value == "" { panic("Missing environment variable: " + name) }
return value
}
func main() {
baseURL := strings.TrimRight(mustEnv("BASE_URL"), "/")
endpoint := baseURL + "/oauth/token"
params := url.Values{}
params.Set("grant_type", "client_credentials")
params.Set("client_id", mustEnv("CLIENT_ID"))
params.Set("client_secret", mustEnv("CLIENT_SECRET"))
body := strings.NewReader(params.Encode())
req, err := http.NewRequest("POST", endpoint, body)
if err != nil { panic(err) }
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{Timeout: 30 * time.Second}
response, err := client.Do(req)
if err != nil { panic(err) }
defer response.Body.Close()
result, err := io.ReadAll(response.Body)
if err != nil { panic(err) }
fmt.Println(string(result))
}
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
public class Example {
static String requireEnv(String name) {
String value = System.getenv(name);
if (value == null || value.isEmpty()) {
throw new IllegalArgumentException("Missing environment variable: " + name);
}
return value;
}
public static void main(String[] args) throws Exception {
String baseUrl = requireEnv("BASE_URL").replaceAll("/+$", "");
String endpoint = baseUrl + "/oauth/token";
Map<String, String> params = new LinkedHashMap<>();
params.put("grant_type", "client_credentials");
params.put("client_id", requireEnv("CLIENT_ID"));
params.put("client_secret", requireEnv("CLIENT_SECRET"));
String encoded = params.entrySet().stream()
.map(e -> URLEncoder.encode(e.getKey(), StandardCharsets.UTF_8) + "="
+ URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&"));
HttpRequest.Builder builder = HttpRequest.newBuilder(URI.create(endpoint))
.timeout(Duration.ofSeconds(30))
.header("Accept", "application/json");
builder.header("Content-Type", "application/x-www-form-urlencoded");
HttpRequest request = builder.method("POST", HttpRequest.BodyPublishers.ofString(encoded, StandardCharsets.UTF_8)).build();
HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
System.out.println(response.body());
}
}
require "net/http"
require "uri"
base_url = ENV.fetch("BASE_URL").sub(%r{/+$}, "")
uri = URI(base_url + "/oauth/token")
params = {
"grant_type" => "client_credentials",
"client_id" => ENV.fetch("CLIENT_ID"),
"client_secret" => ENV.fetch("CLIENT_SECRET"),
}
request = Net::HTTP::Post.new(uri)
request["Accept"] = "application/json"
request.set_form_data(params)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == "https"
http.open_timeout = 30
http.read_timeout = 30
puts http.request(request).body
{
"token_type": "Bearer",
"expires_in": 7200,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiI4YTZiZjJkOS00N2MxLTRlODUtOTJiMy02ZDFhN2MwODRmNTIiLCJqdGkiOiJhZGRiNDgzNDkxMGM2MThjZmJiNGRhMTJhZDMwNWEwNGU3ZmZiNWUzMTE3YjI3YmQwNTVlMjk5ZTgzOTU1ZTkyIiwiaWF0IjoxODkzNDUyNDAwLCJuYmYiOjE4OTM0NTI0MDAsImV4cCI6MTg5MzQ1OTYwMCwic3ViIjoiOGE2YmYyZDktNDdjMS00ZTg1LTkyYjMtNmQxYTdjMDg0ZjUyIiwic2NvcGVzIjpbXX0.dLrN3edou1KGfN5a2kbfDRtGA-iM4meEfwlMTPc1UHv1wGrv70ue8Mb3UU0TYxbZHxK-kHHUVH9aWwItbsyYBkou3Sgr5NAwejvuxSOmQZHk4dT-Uw982pelDqBrBmJV5qZ5qY7qeGDc0Fz_0ymre50BLOd2d3MsLj-INdwjNNIF4fr2AG9M6mDK8pEqGMNMqT20GGVEQ1YJZGwpy-XiPp-6sVS3qOjyineqTK_hN3N0QXykoEM4iFGK_wQdc681B8tfXJRpcQksNt4A1pM2G5nUdnBC-ahByGYUaij4JAkxR_LpnXoR1xQbd-j4wj8cG78WAPdJTzZcIFPRU0Gmjw"
}{
"error": "invalid_request",
"error_description": "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed.",
"hint": "Check the `grant_type` parameter"
}{
"error": "invalid_client",
"error_description": "Client authentication failed"
}{
"message": "Too Many Attempts."
}Body
OAuth form.
Grant type. Always client_credentials.
client_credentials 1OAuth Client ID assigned by BillBuddy. Submit as a string.
1OAuth Client Secret. Store only on the merchant server.
1Optional space-separated scopes. Usually omitted or an empty string.
Response
Token issued successfully.
Standard OAuth success response. Does not contain code/message/data and does not return refresh_token.