Image
how to start

How to Generate Tokens and Make API Requests to Circle K API's

This guide provides step-by-step examples for generating and using an Okta token, which is required to make API requests to Circle K’s services.

In this document, you’ll find instructions for generating tokens using the following programming languages:

  • Python

  • Java

Before running any of the examples, please ensure you have the following information:

  • CLIENT_ID – The Okta client ID for your API user, provided by the Circle K API team during the onboarding process.
    Example: 0oa9ywfrsjocYDtGN0x2

  • TOKEN_ENDPOINT – The URL of the Okta "token" endpoint, also provided during the onboarding process.
    Example: https://login.qa.circlek.eu/oauth2/aus4j5ruks4P09nKP0x7/v1/token

  • SCOPE – The Okta scope. This may be a single value (e.g., eu_b2b_external_transaction_service) or a space-separated list of scopes if multiple are required (depending on the API that you are integrating with).

  • PRIVATE_KEY – The private key you generated and shared with the Circle K team when requesting access to the API.
    Example: qa-private.pem

  • API_URL – The URL of the target API.
    Example: https://api.uat.circlek.com/eu/transaction-api

If you have any further questions, please contact our support team at:
https://circlek.atlassian.net/helpcenter/APISUPPORT/

Token generation examples

  • Python

    import datetime
    import requests
    import jwt # please use PyJWT

    # Configure your app
    CLIENT_ID = "<PLACEHOLDER>" # Fill this placeholder with your Okta client ID ( example: 0oa9ywfrsjocYDtGN0x2 )
    TOKEN_ENDPOINT = f"<PLACEHOLDER>" # Fill this placeholder with the Okta token URL ( example: https://login.qa.circlek.eu/oauth2/aus4j5ruks4P09nKP0x7/v1/token )
    SCOPE = '<PLACEHOLDER>' # Fill this placeholder with the Okta scope (example: eu_b2b_external_transaction_service or space separated list of scopes for your app if there is more than one scope needed)
    PRIVATE_KEY_PATH = '<PLACEHOLDER>' # Fill this placeholder with the file containing your PRIVATE key (example: qa-private.pem)
    API_URL = "<PLACEHOLDER>" # Fill this placeholder with the API URL (example: https://api.uat.circlek.com/eu/transaction-api)
    GRANT = "client_credentials"


    with open(PRIVATE_KEY_PATH, 'r') as f:
       private_key = f.read()

    payload = {
           "iss": CLIENT_ID, 
           "aud": TOKEN_ENDPOINT, 
           "sub": CLIENT_ID,
           "iat": datetime.datetime.now(tz=datetime.timezone.utc),
           "exp": datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(seconds=3000)
       }
    #sign client assertion payload with the private key
    client_assertion = jwt.encode(
       payload,
       private_key,
       algorithm="ES256")
    data = {
       "scope": SCOPE,
       "grant_type": GRANT,
       "client_assertion": client_assertion,
       "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
    }
    print("*** REQUESTING OKTA TOKEN ***")
    response = requests.post(TOKEN_ENDPOINT, data=data)
    print("ACCESS TOKEN STATUS CODE:", response.status_code)
    print("ACCESS TOKEN:", response.json()["access_token"])
    print("DECODED TOKEN:", jwt.decode(response.json()["access_token"],options={"verify_signature": False}))
    print("")


    # URL to ping
    ping_url = API_URL + "/ping"

    # Okta access token
    access_token = response.json()["access_token"]

    # Headers with Bearer token
    headers = {
       "Authorization": f"Bearer {access_token}"
    }

    # Make the GET request
    print("*** CALLING PING ENDPOINT USING OBTAINED OKTA TOKEN ***")
    response = requests.get(ping_url, headers=headers)

    # Print the response status and body
    print("URL:", ping_url)
    print(f"STATUS CODE: {response.status_code}")
    print(f"RESPONSE BODY: {response.text}")

  • Java

    ---------------------------
          Java code
    ---------------------------

    package org.example;

    import com.nimbusds.jose.JOSEObjectType;
    import com.nimbusds.jose.JWSAlgorithm;
    import com.nimbusds.jose.JWSHeader;
    import com.nimbusds.jose.crypto.ECDSASigner;
    import com.nimbusds.jwt.JWTClaimsSet;
    import com.nimbusds.jwt.SignedJWT;
    import java.io.IOException;
    import java.io.Reader;
    import java.io.StringReader;
    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.security.KeyPair;
    import java.security.Security;
    import java.security.interfaces.ECPrivateKey;
    import java.time.Instant;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import org.bouncycastle.openssl.PEMKeyPair;
    import org.bouncycastle.openssl.PEMParser;
    import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;

    public class Main {

       // === CONFIGURE THESE ===
       private static final String CLIENT_ID = "<PLACEHOLDER>"; // Fill this placeholder with your Okta client ID ( example: 0oa9ywfrsjocYDtGN0x2 )
       private static final String TOKEN_ENDPOINT = "<PLACEHOLDER>"; // Fill this placeholder with the Okta token URL ( example: https://login.qa.circlek.eu/oauth2/aus4j5ruks4P09nKP0x7/v1/token )
       private static final String SCOPE = "<PLACEHOLDER>" ; // Fill this placeholder with the Okta scope (example: eu_b2b_external_transaction_service or space separated list of scopes for your app if there is more than one scope needed)
       private static final String PRIVATE_KEY_PATH = "<PLACEHOLDER>"; // Fill this placeholder with the file containing your PRIVATE key (example: qa-private.pem)
       private static final String API_URL = "<PLACEHOLDER>"; // # Fill this placeholder with the API URL (example: https://api.uat.circlek.com/eu/transaction-api)
       private static final String GRANT_TYPE = "client_credentials";

       static {
           Security.addProvider(new BouncyCastleProvider());
       }

       public static void main(String[] args) throws Exception {
           String privateKeyPem = Files.readString(Paths.get(PRIVATE_KEY_PATH));
           ECPrivateKey privateKey = loadECPrivateKey(privateKeyPem);

           // 1. Create JWT client assertion
           String clientAssertion = createClientAssertion(privateKey);

           // 2. Request Access Token
           String accessToken = requestAccessToken(clientAssertion);

           // 3. Call Ping Endpoint
           callPingEndpoint(accessToken);
       }

       private static ECPrivateKey loadECPrivateKey(String pem) throws Exception {
           Reader rdr = new StringReader(pem);
           Object parsed = new PEMParser(rdr).readObject();
           KeyPair pair = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair) parsed);
           return (ECPrivateKey) pair.getPrivate();
       }

       private static String createClientAssertion(ECPrivateKey privateKey) throws Exception {
           Instant now = Instant.now();

           JWTClaimsSet claims = new JWTClaimsSet.Builder()
                   .issuer(CLIENT_ID)
                   .subject(CLIENT_ID)
                   .audience(TOKEN_ENDPOINT)
                   .issueTime(java.util.Date.from(now))
                   .expirationTime(java.util.Date.from(now.plusSeconds(3000)))
                   .build();

           JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES256)
                   .type(JOSEObjectType.JWT)
                   .build();

           SignedJWT signedJWT = new SignedJWT(header, claims);
           signedJWT.sign(new ECDSASigner(privateKey));

           return signedJWT.serialize();
       }

       private static String requestAccessToken(String clientAssertion) throws IOException, InterruptedException {
           HttpClient client = HttpClient.newHttpClient();

           String form = "scope=" + SCOPE +
                   "&grant_type=" + GRANT_TYPE +
                   "&client_assertion=" + clientAssertion +
                   "&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer";

           HttpRequest request = HttpRequest.newBuilder()
                   .uri(URI.create(TOKEN_ENDPOINT))
                   .header("Content-Type", "application/x-www-form-urlencoded")
                   .POST(HttpRequest.BodyPublishers.ofString(form))
                   .build();

           System.out.println("*** REQUESTING OKTA TOKEN ***");
           HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
           System.out.println("ACCESS TOKEN RESPONSE: " + response.body());

           // Basic extraction of token (ideally use JSON parser)
           return extractTokenFromJson(response.body());
       }

       private static String extractTokenFromJson(String json) {
           // Super basic parser (use Jackson or Gson in production)
           String marker = "\"access_token\":\"";
           int start = json.indexOf(marker) + marker.length();
           int end = json.indexOf("\"", start);
           return json.substring(start, end);
       }

       private static void callPingEndpoint(String accessToken) throws IOException, InterruptedException {
           HttpClient client = HttpClient.newHttpClient();
           String pingUrl = API_URL + "/ping";

           HttpRequest request = HttpRequest.newBuilder()
                   .uri(URI.create(pingUrl))
                   .header("Authorization", "Bearer " + accessToken)
                   .GET()
                   .build();

           System.out.println("*** CALLING PING ENDPOINT USING OBTAINED OKTA TOKEN ***");
           HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
           System.out.println("URL: " + pingUrl);
           System.out.println("STATUS CODE: " + response.statusCode());
           System.out.println("RESPONSE BODY: " + response.body());
       }
    }

    ---------------------------
    POM dependencies
    ---------------------------

        <dependencies>
           <dependency>
               <groupId>com.nimbusds</groupId>
               <artifactId>nimbus-jose-jwt</artifactId>
               <version>9.37.3</version>
           </dependency>
           <dependency>
               <groupId>org.bouncycastle</groupId>
               <artifactId>bcpkix-jdk18on</artifactId>
               <version>1.81</version>
           </dependency>
       </dependencies>