Exchange credentials for a JWT
curl --request POST \
--url https://api.pontisglobe.com/api/v1/user/login \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--header 'x-signature: <x-signature>' \
--header 'x-timestamp: <x-timestamp>' \
--data '
{
"email": "jsmith@example.com",
"password": "<string>"
}
'import requests
url = "https://api.pontisglobe.com/api/v1/user/login"
payload = {
"email": "jsmith@example.com",
"password": "<string>"
}
headers = {
"x-timestamp": "<x-timestamp>",
"x-signature": "<x-signature>",
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-timestamp': '<x-timestamp>',
'x-signature': '<x-signature>',
'x-api-key': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({email: 'jsmith@example.com', password: '<string>'})
};
fetch('https://api.pontisglobe.com/api/v1/user/login', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.pontisglobe.com/api/v1/user/login",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'email' => 'jsmith@example.com',
'password' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>",
"x-signature: <x-signature>",
"x-timestamp: <x-timestamp>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.pontisglobe.com/api/v1/user/login"
payload := strings.NewReader("{\n \"email\": \"jsmith@example.com\",\n \"password\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-timestamp", "<x-timestamp>")
req.Header.Add("x-signature", "<x-signature>")
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.pontisglobe.com/api/v1/user/login")
.header("x-timestamp", "<x-timestamp>")
.header("x-signature", "<x-signature>")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"jsmith@example.com\",\n \"password\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.pontisglobe.com/api/v1/user/login")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-timestamp"] = '<x-timestamp>'
request["x-signature"] = '<x-signature>'
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"jsmith@example.com\",\n \"password\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"data": {
"access_token": "eyJhbGciOi…",
"token_type": "Bearer",
"expires_in": 900
}
}{
"ok": false,
"request_id": "8c1f4b2a-9d3e-4a71-b6c8-5e2f0a7d1934",
"error": {
"code": "insufficient_funds",
"message": "Insufficient available balance. Available: 5.00 USDT"
}
}{
"ok": false,
"request_id": "8c1f4b2a-9d3e-4a71-b6c8-5e2f0a7d1934",
"error": {
"code": "insufficient_funds",
"message": "Insufficient available balance. Available: 5.00 USDT"
}
}{
"ok": false,
"request_id": "8c1f4b2a-9d3e-4a71-b6c8-5e2f0a7d1934",
"error": {
"code": "insufficient_funds",
"message": "Insufficient available balance. Available: 5.00 USDT"
}
}{
"ok": false,
"request_id": "8c1f4b2a-9d3e-4a71-b6c8-5e2f0a7d1934",
"error": {
"code": "insufficient_funds",
"message": "Insufficient available balance. Available: 5.00 USDT"
}
}Payouts
/api/v1/user/login
Exchange your account credentials for a 15-minute JWT. Required for payouts only.
POST
/
api
/
v1
/
user
/
login
Exchange credentials for a JWT
curl --request POST \
--url https://api.pontisglobe.com/api/v1/user/login \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--header 'x-signature: <x-signature>' \
--header 'x-timestamp: <x-timestamp>' \
--data '
{
"email": "jsmith@example.com",
"password": "<string>"
}
'import requests
url = "https://api.pontisglobe.com/api/v1/user/login"
payload = {
"email": "jsmith@example.com",
"password": "<string>"
}
headers = {
"x-timestamp": "<x-timestamp>",
"x-signature": "<x-signature>",
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-timestamp': '<x-timestamp>',
'x-signature': '<x-signature>',
'x-api-key': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({email: 'jsmith@example.com', password: '<string>'})
};
fetch('https://api.pontisglobe.com/api/v1/user/login', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.pontisglobe.com/api/v1/user/login",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'email' => 'jsmith@example.com',
'password' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>",
"x-signature: <x-signature>",
"x-timestamp: <x-timestamp>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.pontisglobe.com/api/v1/user/login"
payload := strings.NewReader("{\n \"email\": \"jsmith@example.com\",\n \"password\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-timestamp", "<x-timestamp>")
req.Header.Add("x-signature", "<x-signature>")
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.pontisglobe.com/api/v1/user/login")
.header("x-timestamp", "<x-timestamp>")
.header("x-signature", "<x-signature>")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"jsmith@example.com\",\n \"password\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.pontisglobe.com/api/v1/user/login")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-timestamp"] = '<x-timestamp>'
request["x-signature"] = '<x-signature>'
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"jsmith@example.com\",\n \"password\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"data": {
"access_token": "eyJhbGciOi…",
"token_type": "Bearer",
"expires_in": 900
}
}{
"ok": false,
"request_id": "8c1f4b2a-9d3e-4a71-b6c8-5e2f0a7d1934",
"error": {
"code": "insufficient_funds",
"message": "Insufficient available balance. Available: 5.00 USDT"
}
}{
"ok": false,
"request_id": "8c1f4b2a-9d3e-4a71-b6c8-5e2f0a7d1934",
"error": {
"code": "insufficient_funds",
"message": "Insufficient available balance. Available: 5.00 USDT"
}
}{
"ok": false,
"request_id": "8c1f4b2a-9d3e-4a71-b6c8-5e2f0a7d1934",
"error": {
"code": "insufficient_funds",
"message": "Insufficient available balance. Available: 5.00 USDT"
}
}{
"ok": false,
"request_id": "8c1f4b2a-9d3e-4a71-b6c8-5e2f0a7d1934",
"error": {
"code": "insufficient_funds",
"message": "Insufficient available balance. Available: 5.00 USDT"
}
}Only the payout endpoints require a JWT. Payin endpoints are
authenticated by your API key, request signature and IP allowlist alone — a payin-only integration
never calls this endpoint.
The fields above are what you encrypt, not what goes on the wire. The body is always
{ "data": "<aes-256-gcm ciphertext>" } — see Authentication.Example request
The snippets below assume you’ve already encrypted the body and computed the signature — see the
Quickstart for the full helper in Node and Python.
const login = await call('/api/v1/user/login', {
email: 'you@example.com',
password: 'YOUR_PASSWORD',
})
status, body = call('/api/v1/user/login', {
'email': 'you@example.com',
'password': 'YOUR_PASSWORD',
})
curl -X POST "https://api.pontisglobe.com/api/v1/user/login" \
-H "content-type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-H "x-timestamp: 1748023400" \
-H "x-signature: 2f8a9b…" \
-d '{"data":"<encrypted blob>"}'
req, _ := http.NewRequest("POST",
"https://api.pontisglobe.com/api/v1/user/login",
strings.NewReader(`{"data":"`+encryptedBody+`"}`))
req.Header.Set("content-type", "application/json")
req.Header.Set("x-api-key", apiKey)
req.Header.Set("x-timestamp", timestamp)
req.Header.Set("x-signature", signature)
res, _ := http.DefaultClient.Do(req)
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://api.pontisglobe.com/api/v1/user/login"))
.header("content-type", "application/json")
.header("x-api-key", apiKey)
.header("x-timestamp", timestamp)
.header("x-signature", signature)
.POST(HttpRequest.BodyPublishers.ofString("{\"data\":\"" + encryptedBody + "\"}"))
.build();
HttpResponse<String> res = HttpClient.newHttpClient().send(req, BodyHandlers.ofString());
val req = Request.Builder()
.url("https://api.pontisglobe.com/api/v1/user/login")
.post("""{"data":"$encryptedBody"}""".toRequestBody("application/json".toMediaType()))
.addHeader("x-api-key", apiKey)
.addHeader("x-timestamp", timestamp)
.addHeader("x-signature", signature)
.build()
val res = OkHttpClient().newCall(req).execute()
$ch = curl_init('https://api.pontisglobe.com/api/v1/user/login');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'content-type: application/json',
"x-api-key: $apiKey",
"x-timestamp: $timestamp",
"x-signature: $signature",
],
CURLOPT_POSTFIELDS => json_encode(['data' => $encryptedBody]),
CURLOPT_RETURNTRANSFER => true,
]);
$res = curl_exec($ch);
uri = URI('https://api.pontisglobe.com/api/v1/user/login')
req = Net::HTTP::Post.new(uri, {
'content-type' => 'application/json',
'x-api-key' => api_key,
'x-timestamp' => timestamp,
'x-signature' => signature,
})
req.body = { data: encrypted_body }.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
var req = new HttpRequestMessage(HttpMethod.Post,
"https://api.pontisglobe.com/api/v1/user/login");
req.Headers.Add("x-api-key", apiKey);
req.Headers.Add("x-timestamp", timestamp);
req.Headers.Add("x-signature", signature);
req.Content = new StringContent($"{{\"data\":\"{encryptedBody}\"}}",
Encoding.UTF8, "application/json");
var res = await new HttpClient().SendAsync(req);
var req = URLRequest(url: URL(string: "https://api.pontisglobe.com/api/v1/user/login")!)
req.httpMethod = "POST"
req.setValue("application/json", forHTTPHeaderField: "content-type")
req.setValue(apiKey, forHTTPHeaderField: "x-api-key")
req.setValue(timestamp, forHTTPHeaderField: "x-timestamp")
req.setValue(signature, forHTTPHeaderField: "x-signature")
req.httpBody = #"{"data":"\#(encryptedBody)"}"#.data(using: .utf8)
let (data, _) = try await URLSession.shared.data(for: req)
Using the token
Passaccess_token as Authorization: Bearer <token> on every payout and beneficiary request. It
expires in 900 seconds and is bound to both your account and the mode implied by the host — a
sandbox token will not work against live.
A 401 here usually means the email, password and API key do not all belong to the same user
account. See Errors for the full envelope shape and code list.Authorizations
Identifies your account. Issued from Developer Tools in the dashboard.
Headers
Unix epoch in seconds — not milliseconds. Must be within ±5 minutes of our clock, which is what makes a captured request unusable later. Keep your client's clock NTP-synced.
Example:
1748023400
HMAC-SHA256 over the signing string, hex encoded. The timestamp is part of what is signed, so a replayed body cannot be re-dated. See https://docs.pontisglobe.com/authentication for how it is built.
Example:
"2f8a9b4c1d7e0a3f6b8c2d5e9f1a4b7c0d3e6f9a2b5c8d1e4f7a0b3c6d9e2f5a"
Body
application/json