agricole
Sign up

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/Secret as 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 Secret to compute the signature.
  • Send the request, including the key request information, the signature and API Key.

Follow these step-to-step instructions to implement the authentication mechanism.

  1. Collect key request information
  2. Prepare date headers
  3. Prepare canonical headers
  4. Build signature subject (create string-to-hash)
  5. Compute signature (calculate hash)
  6. Build authorisation header
  7. 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

Skip code example Copy
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

Skip code example Copy
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)

Skip code example Copy
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)

Skip code example Copy
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

Was this page helpful?

Do you have any comments?

Thank you for your response.