Manual authentication (no SDK)
If you choose not to use our SDKs for sending requests to our endpoints, you need to implement the authentication mechanism on your own.
The CAWL ecommerce API uses HMAC-SHA256 request signing, a keyed hash using SHA-256: You compute a cryptographic signature and include it in the request. This signature is based on
- Your credentials (API Key/Secret).
- Key request information in your request.
Upon receiving your request, our platform recalculates this very signature. If both signature match, we authenticate the request.
Implementation
To implement the authentication mechanism, you need to
- Create a
API Key/Secretas described in the "API key/secret configuration" chapter in our Authentication guide. - Prepare the key request information: HTTP method, request path, and required request headers.
- Hash the key request information with the
API Secretto compute thesignature. - Send the request, including the key request information, the
signatureandAPI Key.
Follow these step-to-step instructions to implement the authentication mechanism.
- Collect key request information
- Prepare date headers
- Prepare canonical headers
- Build signature subject (create string-to-hash)
- Compute signature (calculate hash)
- Build authorisation header
- Send request
Collect key request information
Collect the key request information for your request to the CAWL ecommerce API:
| Property | Description |
|---|---|
| HTTP method | Depending on the endpoint you want to send the request to, i.e. POST, GET etc. |
| Content-type header |
Fixed value "application/json" for request with a body (i.e. POST). Empty string for requests without a body (i.e. GET). |
| Date header | UTC timestamp in RFC 1123 format. Must be identical for both request header and the string-to-hash to compute the signature. |
| x-gcs-* headers | Any x-gcs-* canonical custom headers you would like to send, sorted alphabetically in the string-to-hash to compute the signature. |
| Request path | The relative path of the CAWL ecommerce API you send the request to, including the API version and your merchantId i.e. "/v2/merchantId/hostedcheckouts" |
Prepare date headers
For the date header, create a UTC timestamp in RFC 1123 format. Use the same value for both the header and the signature subject (the string-to-hash).
Our platform will reject requests with a timestamp older than 5 minutes.
Prepare canonical headers
If you include any CAWL-specifc x-gcs-* headers in your request, you need to normalise them according to these conventions:
- Lowercase the header key.
- Trim leading/trailing whitespace from the header value.
- Collapse any internal whitespace runs in the header value to a single space.
- Format each header key-value pair as {lowercased-key}:{normalized-value}.
- Sort the canonical key-value pairs alphabetically by key.
If you include any x-gcs-* header in the signature subject, make sure to add it in the actual request.
| Convention | Raw value | Normalised key/value |
|---|---|---|
| Lowercase header key | X-GCS-Idempotency-Key | x-gcs-idempotency-key |
| Trim leading/trailing whitespace header value | ··header value·· | header value |
| Collapse whitespace in header value | header··value | header value |
| Key-value pair formatting | {raw-key}={raw-value} | {lowercased-key}:{normalized-value} |
| Alphabetical order of canonical key-value pairs | x-gcs-serverdatetime:2026-05-18T10:00:00Z x-gcs-idempotency-key:abc 123 x-gcs-date:Mon, 18 May 2026 10:00:00 GMT |
x-gcs-date:Mon, 18 May 2026 10:00:00 GMT x-gcs-idempotency-key:abc 123 x-gcs-serverdatetime:2026-05-18T10:00:00Z |
Our examples use only the x-gcs-idempotency-key header to show how to handle CAWL-specific x-gcs-* headers.
If you use our Server SDKs to call the CAWL ecommerce API, they automatically add and handle additional x-gcs-* headers.
These extra headers are not relevant for manual authentication, so we do not cover them here.
Build signature subject (create string-to-hash)
Concatenate the following elements (request method, headers, canonical headers, request path) in this order, each terminated by a newline character \n:
{HTTP_METHOD}\n
{Content-Type}\n
{Date}\n
{canonical-x-gcs-header-1}\n
{canonical-x-gcs-header-N}\n
{request-path}\n
- If you include canonical x-gcs-* headers, make sure to order them by key as described above.
- Content-Type is always an empty string for requests without a body.
Typical examples for requests with/without a body look like this:
Request with a body (e.g. POST, PUT, PATCH)
POST\n
application/json\n
Mon, 18 May 2026 10:00:00 GMT\n
x-gcs-date:Mon, 18 May 2026 10:00:00 GMT\n
x-gcs-idempotency-key:<unique-idempotency-key>\n
/v1/{merchantId}/payments\n
Request without a body (e.g. GET, DELETE)
GET\n
\n
Mon, 18 May 2026 10:00:00 GMT\n
x-gcs-date:Mon, 18 May 2026 10:00:00 GMT\n
/v1/{merchantId}/payments/{paymentId}\n
Compute signature (calculate hash)
Hash the signature subject (string-to-hash) using HMAC-SHA256 (UTF-8 encoding) with your API Secret. Then Base64-encode the resulting signature:
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
// The following code should be placed inside a method that declares: throws Exception
String apiSecret = "YourAPISecret";
String signatureSubject =
"POST" + "\n" +
"application/json" + "\n" +
"Mon, 18 May 2026 10:00:00 GMT" + "\n" +
"x-gcs-date:Mon, 18 May 2026 10:00:00 GMT" + "\n" +
"x-gcs-idempotency-key:" + "\n" +
"/v1/{merchantId}/payments" + "\n";
// Convert apiSecret / signatureSubject into byte arrays
byte[] keyBytes = apiSecret.getBytes(StandardCharsets.UTF_8);
byte[] subjectBytes = signatureSubject.getBytes(StandardCharsets.UTF_8);
// Hash the signatureSubject
Mac hmac = Mac.getInstance("HmacSHA256");
hmac.init(new SecretKeySpec(keyBytes, "HmacSHA256"));
String signature = Base64.getEncoder().encodeToString(hmac.doFinal(subjectBytes));
using System;
using System.Security.Cryptography;
using System.Text;
string apiSecret = "YourAPISecret";
string signatureSubject =
"POST" + "\n" +
"application/json" + "\n" +
"Mon, 18 May 2026 10:00:00 GMT" + "\n" +
"x-gcs-date:Mon, 18 May 2026 10:00:00 GMT" + "\n" +
"x-gcs-idempotency-key:" + "\n" +
"/v1/{merchantId}/payments" + "\n";
// Convert apiSecret / signatureSubject into byte arrays
byte[] keyBytes = Encoding.UTF8.GetBytes(apiSecret);
byte[] subjectBytes = Encoding.UTF8.GetBytes(signatureSubject);
// Hash the signatureSubject
using HMACSHA256 hmac = new HMACSHA256(keyBytes);
string signature = Convert.ToBase64String(hmac.ComputeHash(subjectBytes));
import crypto from 'crypto';
const apiSecret = 'YourAPISecret';
const signatureSubject =
'POST' + '\n' +
'application/json' + '\n' +
'Mon, 18 May 2026 10:00:00 GMT' + '\n' +
'x-gcs-date:Mon, 18 May 2026 10:00:00 GMT' + '\n' +
'x-gcs-idempotency-key:' + '\n' +
'/v1/{merchantId}/payments' + '\n';
// Hash the signatureSubject
const signature = crypto
.createHmac('SHA256', apiSecret)
.update(signatureSubject)
.digest('base64');
' . "\n" .
'/v1/{merchantId}/payments' . "\n";
// Hash the signatureSubject
$signature = base64_encode(hash_hmac('sha256', $signatureSubject, $apiSecret, true));
import hmac
import hashlib
from base64 import b64encode
api_secret = 'YourAPISecret'
signature_subject = (
'POST' + '\n' +
'application/json' + '\n' +
'Mon, 18 May 2026 10:00:00 GMT' + '\n' +
'x-gcs-date:Mon, 18 May 2026 10:00:00 GMT' + '\n' +
'x-gcs-idempotency-key:' + '\n' +
'/v1/{merchantId}/payments' + '\n'
)
# Hash the signature_subject
signature = b64encode(
hmac.new(api_secret.encode('utf-8'), signature_subject.encode('utf-8'), hashlib.sha256).digest()
).decode('utf-8')
require 'openssl'
require 'base64'
api_secret = 'YourAPISecret'
signature_subject =
'POST' + "\n" +
'application/json' + "\n" +
'Mon, 18 May 2026 10:00:00 GMT' + "\n" +
'x-gcs-date:Mon, 18 May 2026 10:00:00 GMT' + "\n" +
'x-gcs-idempotency-key:' + "\n" +
'/v1/{merchantId}/payments' + "\n"
# Hash the signature_subject
digest = OpenSSL::Digest.new('SHA256')
signature = Base64.strict_encode64(OpenSSL::HMAC.digest(digest, api_secret, signature_subject)).strip
Build authorisation header
Add the Authorisation GCS v1HMAC header to your request, consisting of the computed signature and your API Key:
Authorization: GCS v1HMAC:<your-api-key>:<base64-encoded-signature>
Send request
When sending your request to the CAWL ecommerce API, include all headers from the signature subject (create string-to-hash) and the Authorisation header.
Raw HTTP request with a body
POST /v1/{merchantId}/payments HTTP/1.1
Host: api.example.com
Authorization: GCS v1HMAC:<your-api-key>:<base64-encoded-signature>
Date: Mon, 18 May 2026 10:00:00 GMT
Content-Type: application/json
x-gcs-date: Mon, 18 May 2026 10:00:00 GMT
x-gcs-idempotency-key: <unique-idempotency-key>
{ ... request body ... }
Raw HTTP request without a body
GET /v1/{merchantId}/payments/{paymentId} HTTP/1.1
Host: api.example.com
Authorization: GCS v1HMAC:<your-api-key>:<base64-encoded-signature>
Date: Mon, 18 May 2026 10:00:00 GMT
x-gcs-date: Mon, 18 May 2026 10:00:00 GMT
Troubleshooting mismatches
Upon receiving your request, our platform tries to match the signature and the API key from the Authorisation header. If this matching fails, our platform will reject your request, throwing an error message like this:
{
"errorId": "error-id",
"errors": [
{
"code": "9007",
"id": "ACCESS_TO_MERCHANT_NOT_ALLOWED",
"category": "DIRECT_PLATFORM_ERROR",
"message": "ACCESS_TO_MERCHANT_NOT_ALLOWED",
"httpStatusCode": 403
}
],
"status": 403
}
To troubleshoot such errors, make sure to follow these guidelines:
- Use the correct environment: Do not mix up API Keys/Secrets/accounts from the test/prod environment.
- Avoid expired API Keys/Secrets.
- Keep the Date header within 5 minutes and use the same date in the header and the signature.
- For requests without a body, set Content-Type to an empty string. Do not omit or set to null.
- Apply the formatting/normalisation rules for x-gcs-* headers.
- Send the same headers in the request that you have used for concatenating the signature subject (string-to-hash).
Refer to our dedicated API Troubleshooting guide for analysing and solving other platform errors.
Full code example
Use this full code example that combines all previous steps into a single working implementation:
Helper class
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;
public class HmacAuthHelper {
private static final List METHODS_WITHOUT_BODY = List.of("GET", "DELETE");
/**
* Builds the GCS v1HMAC Authorization header value.
*
* @param method HTTP method (e.g. "POST")
* @param path Request path (e.g. "/v1/{entityId}/payments")
* @param contentType Content-Type header value; ignored for body-less methods
* @param date RFC 1123 UTC date string — must match the Date header sent with the request
* @param apiKeyId Your API Key ID
* @param apiSecret Your API Secret
* @param xGcsHeaders Optional x-gcs-* headers as raw "Key: Value" strings
*/
public static String buildAuthorizationHeader(
String method, String path, String contentType, String date,
String apiKeyId, String apiSecret, List xGcsHeaders) throws Exception {
// Normalize Content-Type — body-less methods always use empty string
if (METHODS_WITHOUT_BODY.contains(method.toUpperCase())) {
contentType = "";
}
// Canonicalize x-gcs-* headers
List canonicalHeaders = xGcsHeaders.stream()
.map(h -> {
int idx = h.indexOf(":");
String key = h.substring(0, idx).trim().toLowerCase();
String value = h.substring(idx + 1).trim().replaceAll("\\s+", " ");
return key + ":" + value;
})
.sorted()
.collect(Collectors.toList());
// Build the signature subject
StringBuilder subject = new StringBuilder();
subject.append(method.toUpperCase()).append("\n"); // method\n
subject.append(contentType).append("\n"); // content-type\n
subject.append(date).append("\n"); // date\n
for (String header : canonicalHeaders) {
subject.append(header).append("\n"); // x-gcs-header\n (one per line)
}
subject.append(path).append("\n"); // path\n
// Sign with HMAC-SHA256 and Base64-encode
byte[] keyBytes = apiSecret.trim().getBytes(StandardCharsets.UTF_8);
byte[] subjectBytes = subject.toString().getBytes(StandardCharsets.UTF_8);
Mac hmac = Mac.getInstance("HmacSHA256");
hmac.init(new SecretKeySpec(keyBytes, "HmacSHA256"));
String signature = Base64.getEncoder().encodeToString(hmac.doFinal(subjectBytes));
return "GCS v1HMAC:" + apiKeyId.trim() + ":" + signature;
}
}
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
public static class HmacAuthHelper
{
private static readonly string[] MethodsWithoutBody = ["GET", "DELETE"];
public static string BuildAuthorizationHeader(
string method,
string path,
string contentType,
string date,
string apiKeyId,
string apiSecret,
params string[] xGcsHeaders)
{
// Content-Type is empty for methods without a request body
if (MethodsWithoutBody.Contains(method.ToUpperInvariant()))
contentType = string.Empty;
// Normalize and sort x-gcs-* headers: lowercase key, collapse whitespace in value
string[] canonicalHeaders = xGcsHeaders
.Select(h => {
int separatorIndex = h.IndexOf(":");
string key = h[..separatorIndex].Trim().ToLowerInvariant();
string value = Regex.Replace(h[(separatorIndex + 1)..].Trim(), @"\s+", " ");
return $"{key}:{value}";
})
.OrderBy(h => h)
.ToArray();
// Build the signature subject string (each element followed by \n)
StringBuilder subject = new StringBuilder();
subject.Append(method.ToUpperInvariant() + "\n"); // method\n
subject.Append(contentType + "\n"); // content-type\n
subject.Append(date + "\n"); // date\n
foreach (string header in canonicalHeaders)
subject.Append(header + "\n"); // x-gcs-header\n (one per line)
subject.Append(path + "\n"); // path\n
// Convert apiSecret / signatureSubject into byte arrays
byte[] keyBytes = Encoding.UTF8.GetBytes(apiSecret.Trim());
byte[] subjectBytes = Encoding.UTF8.GetBytes(subject.ToString());
// Hash the signatureSubject and Base64-encode the result
using HMACSHA256 hmac = new HMACSHA256(keyBytes);
string signature = Convert.ToBase64String(hmac.ComputeHash(subjectBytes));
return $"GCS v1HMAC:{apiKeyId.Trim()}:{signature}";
}
}
import crypto from 'crypto';
const METHODS_WITHOUT_BODY = ['GET', 'DELETE'];
function buildAuthorizationHeader(method, path, contentType, date, apiKeyId, apiSecret, xGcsHeaders = []) {
// Normalize Content-Type — body-less methods always use empty string
if (METHODS_WITHOUT_BODY.includes(method.toUpperCase())) {
contentType = '';
}
// Canonicalize x-gcs-* headers
const canonicalHeaders = xGcsHeaders
.map(h => {
const idx = h.indexOf(':');
const key = h.substring(0, idx).trim().toLowerCase();
const value = h.substring(idx + 1).trim().replace(/\s+/g, ' ');
return `${key}:${value}`;
})
.sort();
// Build the signature subject
let subject = method.toUpperCase() + '\n'; // method\n
subject += contentType + '\n'; // content-type\n
subject += date + '\n'; // date\n
for (const header of canonicalHeaders) {
subject += header + '\n'; // x-gcs-header\n (one per line)
}
subject += path + '\n'; // path\n
// Sign with HMAC-SHA256 and Base64-encode
const signature = crypto
.createHmac('SHA256', apiSecret.trim())
.update(subject)
.digest('base64');
return `GCS v1HMAC:${apiKeyId.trim()}:${signature}`;
}
<?php
class HmacAuthHelper
{
private static array $methodsWithoutBody = ['GET', 'DELETE'];
public static function buildAuthorizationHeader(
string $method,
string $path,
string $contentType,
string $date,
string $apiKeyId,
string $apiSecret,
array $xGcsHeaders = []
): string {
// Normalize Content-Type — body-less methods always use empty string
if (in_array(strtoupper($method), self::$methodsWithoutBody)) {
$contentType = '';
}
// Canonicalize x-gcs-* headers
$canonicalHeaders = [];
foreach ($xGcsHeaders as $h) {
$idx = strpos($h, ':');
$key = strtolower(trim(substr($h, 0, $idx)));
$value = preg_replace('/\s+/', ' ', trim(substr($h, $idx + 1)));
$canonicalHeaders[] = $key . ':' . $value;
}
sort($canonicalHeaders);
// Build the signature subject
$subject = strtoupper($method) . "\n"; // method\n
$subject .= $contentType . "\n"; // content-type\n
$subject .= $date . "\n"; // date\n
foreach ($canonicalHeaders as $header) {
$subject .= $header . "\n"; // x-gcs-header\n (one per line)
}
$subject .= $path . "\n"; // path\n
// Sign with HMAC-SHA256 and Base64-encode
$signature = base64_encode(hash_hmac('sha256', $subject, trim($apiSecret), true));
return 'GCS v1HMAC:' . trim($apiKeyId) . ':' . $signature;
}
}
import hmac
import hashlib
import re
from base64 import b64encode
class HmacAuthHelper:
METHODS_WITHOUT_BODY = {'GET', 'DELETE'}
@staticmethod
def build_authorization_header(
method: str,
path: str,
content_type: str,
date: str,
api_key_id: str,
api_secret: str,
x_gcs_headers: list[str] = None) -> str:
if x_gcs_headers is None:
x_gcs_headers = []
# Normalize Content-Type — body-less methods always use empty string
if method.upper() in HmacAuthHelper.METHODS_WITHOUT_BODY:
content_type = ''
# Canonicalize x-gcs-* headers
canonical_headers = []
for h in x_gcs_headers:
idx = h.index(':')
key = h[:idx].strip().lower()
value = re.sub(r'\s+', ' ', h[idx + 1:].strip())
canonical_headers.append(f'{key}:{value}')
canonical_headers.sort()
# Build the signature subject
subject = method.upper() + '\n' # method\n
subject += content_type + '\n' # content-type\n
subject += date + '\n' # date\n
for header in canonical_headers:
subject += header + '\n' # x-gcs-header\n (one per line)
subject += path + '\n' # path\n
# Sign with HMAC-SHA256 and Base64-encode
signature = b64encode(
hmac.new(api_secret.strip().encode('utf-8'), subject.encode('utf-8'), hashlib.sha256).digest()
).decode('utf-8')
return f'GCS v1HMAC:{api_key_id.strip()}:{signature}'
require 'openssl'
require 'base64'
module HmacAuthHelper
METHODS_WITHOUT_BODY = %w[GET DELETE].freeze
# Builds the GCS v1HMAC Authorization header value.
#
# @param method [String] HTTP method (e.g. 'POST')
# @param path [String] Request path (e.g. '/v1/{entityId}/payments')
# @param content_type [String] Content-Type header value; ignored for body-less methods
# @param date [String] RFC 1123 UTC date string — must match the Date header sent with the request
# @param api_key_id [String] Your API Key ID
# @param api_secret [String] Your API Secret
# @param x_gcs_headers [Array] Optional x-gcs-* headers as raw 'Key: Value' strings
def self.build_authorization_header(method, path, content_type, date, api_key_id, api_secret, x_gcs_headers = [])
# Normalize Content-Type — body-less methods always use empty string
content_type = '' if METHODS_WITHOUT_BODY.include?(method.upcase)
# Canonicalize x-gcs-* headers
canonical_headers = x_gcs_headers.map do |h|
idx = h.index(':')
key = h[0, idx].strip.downcase
value = h[(idx + 1)..].strip.gsub(/\s+/, ' ')
"#{key}:#{value}"
end.sort
# Build the signature subject
subject = "#{method.upcase}\n" # method\n
subject += "#{content_type}\n" # content-type\n
subject += "#{date}\n" # date\n
canonical_headers.each { |header| subject += "#{header}\n" } # x-gcs-header\n (one per line)
subject += "#{path}\n" # path\n
# Sign with HMAC-SHA256 and Base64-encode
digest = OpenSSL::Digest.new('SHA256')
signature = Base64.strict_encode64(
OpenSSL::HMAC.digest(digest, api_secret.strip, subject)
).strip
"GCS v1HMAC:#{api_key_id.strip}:#{signature}"
end
end
Usage — request with a body (POST)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
// The following code should be placed inside a method that declares: throws Exception
String date = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneOffset.UTC));
// e.g. "Mon, 18 May 2026 10:00:00 GMT"
String authorization = HmacAuthHelper.buildAuthorizationHeader(
"POST",
"/v1/{entityId}/payments",
"application/json",
date,
"",
"",
List.of(
"X-GCS-Date: " + date,
"X-GCS-Idempotency-Key: abc-def-456"
)
);
// Send the request — Date header must match exactly what was passed above
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("/v1/{entityId}/payments"))
.header("Authorization", authorization)
.header("Date", date)
.header("X-GCS-Date", date)
.header("X-GCS-Idempotency-Key", "abc-def-456")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{ ... }"))
.build();
HttpResponse response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
using System;
using System.Net.Http;
using System.Text;
string date = DateTime.UtcNow.ToString("R");
// e.g. "Mon, 18 May 2026 10:00:00 GMT"
string authorization = HmacAuthHelper.BuildAuthorizationHeader(
method: "POST",
path: "/v1/{entityId}/payments",
contentType: "application/json",
date: date,
apiKeyId: "",
apiSecret: "",
xGcsHeaders:
[
$"X-GCS-Date: {date}",
"X-GCS-Idempotency-Key: abc-def-456"
]
);
// Send the request — Date header must match exactly what was passed above
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "/v1/{entityId}/payments");
request.Headers.TryAddWithoutValidation("Authorization", authorization);
request.Headers.TryAddWithoutValidation("Date", date);
request.Headers.TryAddWithoutValidation("X-GCS-Date", date);
request.Headers.TryAddWithoutValidation("X-GCS-Idempotency-Key", "abc-def-456");
request.Content = new StringContent("{ ... }", Encoding.UTF8, "application/json");
const date = new Date().toUTCString();
// e.g. "Mon, 18 May 2026 10:00:00 GMT"
const authorization = buildAuthorizationHeader(
'POST',
'/v1/{entityId}/payments',
'application/json',
date,
'',
'',
[
`X-GCS-Date: Jul 9, 2026, 7:27:23 PM`,
'X-GCS-Idempotency-Key: abc-def-456',
]
);
// Send the request — Date header must match exactly what was passed above
const response = await fetch('/v1/{entityId}/payments', {
method: 'POST',
headers: {
'Authorization': authorization,
'Date': date,
'X-GCS-Date': date,
'X-GCS-Idempotency-Key': 'abc-def-456',
'Content-Type': 'application/json',
},
body: '{ ... }',
});
',
'',
[
"X-GCS-Date: Jul 9, 2026, 7:27:23 PM",
'X-GCS-Idempotency-Key: abc-def-456',
]
);
// Send the request — Date header must match exactly what was passed above
$ch = curl_init('/v1/{entityId}/payments');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{ ... }');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: $authorization",
"Date: Jul 9, 2026, 7:27:23 PM",
"X-GCS-Date: Jul 9, 2026, 7:27:23 PM",
'X-GCS-Idempotency-Key: abc-def-456',
'Content-Type: application/json',
]);
org.tuckey.web.filters.urlrewrite.UrlRewriteWrappedResponse@1a7e251c = curl_exec($ch);
curl_close($ch);
import urllib.request
from email.utils import formatdate
date = formatdate(usegmt=True)
# e.g. "Mon, 18 May 2026 10:00:00 GMT"
authorization = HmacAuthHelper.build_authorization_header(
method='POST',
path='/v1/{entityId}/payments',
content_type='application/json',
date=date,
api_key_id='',
api_secret='',
x_gcs_headers=[
f'X-GCS-Date: {date}',
'X-GCS-Idempotency-Key: abc-def-456',
]
)
# Send the request — Date header must match exactly what was passed above
request = urllib.request.Request(
'/v1/{entityId}/payments',
method='POST',
data=b'{ ... }',
headers={
'Authorization': authorization,
'Date': date,
'X-GCS-Date': date,
'X-GCS-Idempotency-Key': 'abc-def-456',
'Content-Type': 'application/json',
}
)
response = urllib.request.urlopen(request)
require 'net/http'
require 'uri'
date = Time.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
# e.g. "Mon, 18 May 2026 10:00:00 GMT"
authorization = HmacAuthHelper.build_authorization_header(
'POST',
'/v1/{entityId}/payments',
'application/json',
date,
'',
'',
[
"X-GCS-Date: #{date}",
'X-GCS-Idempotency-Key: abc-def-456'
]
)
# Send the request — Date header must match exactly what was passed above
uri = URI('/v1/{entityId}/payments')
request = Net::HTTP::Post.new(uri)
request['Authorization'] = authorization
request['Date'] = date
request['X-GCS-Date'] = date
request['X-GCS-Idempotency-Key'] = 'abc-def-456'
request['Content-Type'] = 'application/json'
request.body = '{ ... }'
response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') { |http| http.request(request) }
Resulting signature subject (before signing):
POST\n
application/json\n
Mon, 18 May 2026 10:00:00 GMT\n
x-gcs-date:Mon, 18 May 2026 10:00:00 GMT\n
x-gcs-idempotency-key:abc-def-456\n
/v1/{entityId}/payments\n
Resulting raw HTTP request:
POST /v1/{entityId}/payments HTTP/1.1
Authorization: GCS v1HMAC:<your-api-key-id>:<base64-encoded-signature>
Date: Mon, 18 May 2026 10:00:00 GMT
Content-Type: application/json
X-GCS-Date: Mon, 18 May 2026 10:00:00 GMT
X-GCS-Idempotency-Key: abc-def-456
{ ... request body ... }
Usage — request without a body (GET)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
// The following code should be placed inside a method that declares: throws Exception
String date = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneOffset.UTC));
String authorization = HmacAuthHelper.buildAuthorizationHeader(
"GET",
"/v1/{entityId}/payments/{paymentId}",
"application/json", // ignored — overridden to "" internally for GET
date,
"",
"",
List.of("X-GCS-Date: " + date)
);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("/v1/{entityId}/payments/{paymentId}"))
.header("Authorization", authorization)
.header("Date", date)
.header("X-GCS-Date", date)
.GET()
.build();
HttpResponse response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
using System;
using System.Net.Http;
string date = DateTime.UtcNow.ToString("R");
string authorization = HmacAuthHelper.BuildAuthorizationHeader(
method: "GET",
path: "/v1/{entityId}/payments/{paymentId}",
contentType: "application/json", // ignored — overridden to "" internally for GET
date: date,
apiKeyId: "",
apiSecret: "",
xGcsHeaders: [$"X-GCS-Date: {date}"]
);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "/v1/{entityId}/payments/{paymentId}");
request.Headers.TryAddWithoutValidation("Authorization", authorization);
request.Headers.TryAddWithoutValidation("Date", date);
request.Headers.TryAddWithoutValidation("X-GCS-Date", date);
const date = new Date().toUTCString();
const authorization = buildAuthorizationHeader(
'GET',
'/v1/{entityId}/payments/{paymentId}',
'application/json', // ignored — overridden to '' internally for GET
date,
'',
'',
[`X-GCS-Date: Jul 9, 2026, 7:27:23 PM`]
);
const response = await fetch('/v1/{entityId}/payments/{paymentId}', {
method: 'GET',
headers: {
'Authorization': authorization,
'Date': date,
'X-GCS-Date': date,
},
});
',
'',
["X-GCS-Date: Jul 9, 2026, 7:27:23 PM"]
);
$ch = curl_init('/v1/{entityId}/payments/{paymentId}');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: $authorization",
"Date: Jul 9, 2026, 7:27:23 PM",
"X-GCS-Date: Jul 9, 2026, 7:27:23 PM",
]);
org.tuckey.web.filters.urlrewrite.UrlRewriteWrappedResponse@1a7e251c = curl_exec($ch);
curl_close($ch);
import urllib.request
from email.utils import formatdate
date = formatdate(usegmt=True)
authorization = HmacAuthHelper.build_authorization_header(
method='GET',
path='/v1/{entityId}/payments/{paymentId}',
content_type='application/json', # ignored — overridden to '' internally for GET
date=date,
api_key_id='',
api_secret='',
x_gcs_headers=[f'X-GCS-Date: {date}']
)
request = urllib.request.Request(
'/v1/{entityId}/payments/{paymentId}',
method='GET',
headers={
'Authorization': authorization,
'Date': date,
'X-GCS-Date': date,
}
)
response = urllib.request.urlopen(request)
require 'net/http'
require 'uri'
date = Time.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
authorization = HmacAuthHelper.build_authorization_header(
'GET',
'/v1/{entityId}/payments/{paymentId}',
'application/json', # ignored — overridden to '' internally for GET
date,
'',
'',
["X-GCS-Date: #{date}"]
)
uri = URI('/v1/{entityId}/payments/{paymentId}')
request = Net::HTTP::Get.new(uri)
request['Authorization'] = authorization
request['Date'] = date
request['X-GCS-Date'] = date
response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') { |http| http.request(request) }
Resulting signature subject (before signing):
GET\n
\n
Mon, 18 May 2026 10:00:00 GMT\n
x-gcs-date:Mon, 18 May 2026 10:00:00 GMT\n
/v1/{entityId}/payments/{paymentId}\n
Resulting raw HTTP request:
GET /v1/{entityId}/payments/{paymentId} HTTP/1.1
Authorization: GCS v1HMAC:<your-api-key-id>:<base64-encoded-signature>
Date: Mon, 18 May 2026 10:00:00 GMT
X-GCS-Date: Mon, 18 May 2026 10:00:00 GMT