Get started

Your API keys

Test and live keys, secret and publishable, and your first authenticated request.

Open as Markdown
On this page
  1. The four kinds of key
  2. Send your first request
  3. When a key is refused
  4. Keep secret keys secret

Every request to the AdProMaxPay API carries a key. The key tells the API which organization you are and which mode you work in, test or live.

The four kinds of key

PrefixKindMode
apx_sk_test_…SecretTest
apx_pk_test_…PublishableTest
apx_sk_live_…SecretLive
apx_pk_live_…PublishableLive
  • A secret key acts for your whole organization. It belongs on your server, never in a web page or an app.
  • A publishable key is made to sit in a web page or an app. Today every API route asks for a secret key.
  • Test keys work as soon as you sign up. Live keys work once your account is activated.

Your sandbox's test secret key is shown once, when you sign up. Later keys are created on the dashboard's API keys page, and a secret key is shown only once, when it is created: store it then.

Send your first request

Put your test secret key in an environment variable, then ask the API who you are. Send the key as a bearer token, or as the user name of HTTP Basic authentication with an empty password.

The answer is your account: its ID, its activation status (sandbox until activation) and the API version it uses.

  • curl
  • Node.js
  • TypeScript
  • Python
  • PHP
  • Ruby
  • Go
  • Java
  • C#
  • Rust
account.mjsaccount.tsaccount.pyaccount.phpaccount.rbaccount.goAccount.javaAccount.csmain.rs
curl https://pay-api.adpromax.com/v1/account \
  -u "$APX_SECRET_KEY:"
const response = await fetch('https://pay-api.adpromax.com/v1/account', {
  headers: { Authorization: `Bearer ${process.env.APX_SECRET_KEY}` },
})
const account = await response.json()
if (!response.ok)
  throw new Error(account.error.message)
console.log(account.id, account.activation_status)
interface Account {
  id: string
  object: 'account'
  business_name: string
  activation_status: string
  api_version: string
}

interface ApiError {
  error: { type: string, code: string, message: string, request_id: string }
}

const response = await fetch('https://pay-api.adpromax.com/v1/account', {
  headers: { Authorization: `Bearer ${process.env.APX_SECRET_KEY}` },
})
if (!response.ok) {
  const { error } = (await response.json()) as ApiError
  throw new Error(`${error.code}: ${error.message}`)
}
const account = (await response.json()) as Account
console.log(account.id, account.activation_status)
import os

import requests

response = requests.get(
    "https://pay-api.adpromax.com/v1/account",
    auth=(os.environ["APX_SECRET_KEY"], ""),
)
account = response.json()
if not response.ok:
    raise RuntimeError(account["error"]["message"])
print(account["id"], account["activation_status"])
<?php
$ch = curl_init('https://pay-api.adpromax.com/v1/account');
curl_setopt_array($ch, [
    CURLOPT_USERPWD => getenv('APX_SECRET_KEY') . ':',
    CURLOPT_RETURNTRANSFER => true,
]);
$account = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_RESPONSE_CODE) >= 400) {
    throw new RuntimeException($account['error']['message']);
}
echo $account['id'], ' ', $account['activation_status'], PHP_EOL;
require 'json'
require 'net/http'

uri = URI('https://pay-api.adpromax.com/v1/account')
request = Net::HTTP::Get.new(uri)
request.basic_auth(ENV.fetch('APX_SECRET_KEY'), '')

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
  http.request(request)
end
account = JSON.parse(response.body)
raise account.dig('error', 'message') unless response.is_a?(Net::HTTPSuccess)

puts "#{account['id']} #{account['activation_status']}"
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
)

type Account struct {
	ID               string `json:"id"`
	BusinessName     string `json:"business_name"`
	ActivationStatus string `json:"activation_status"`
	APIVersion       string `json:"api_version"`
}

type APIError struct {
	Error struct {
		Code    string `json:"code"`
		Message string `json:"message"`
	} `json:"error"`
}

func main() {
	req, err := http.NewRequest(http.MethodGet, "https://pay-api.adpromax.com/v1/account", nil)
	if err != nil {
		log.Fatal(err)
	}
	req.SetBasicAuth(os.Getenv("APX_SECRET_KEY"), "")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()

	if res.StatusCode >= 400 {
		var apiErr APIError
		json.NewDecoder(res.Body).Decode(&apiErr)
		log.Fatal(apiErr.Error.Message)
	}
	var account Account
	if err := json.NewDecoder(res.Body).Decode(&account); err != nil {
		log.Fatal(err)
	}
	fmt.Println(account.ID, account.ActivationStatus)
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Account {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://pay-api.adpromax.com/v1/account"))
            .header("Authorization", "Bearer " + System.getenv("APX_SECRET_KEY"))
            .GET()
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            throw new IllegalStateException(response.body());
        }
        // The account as JSON: read it with your JSON library (Jackson, Gson…).
        System.out.println(response.body());
    }
}
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;

var json = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower };
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Bearer", Environment.GetEnvironmentVariable("APX_SECRET_KEY"));

using var response = await http.GetAsync("https://pay-api.adpromax.com/v1/account");
if (!response.IsSuccessStatusCode)
{
    var failure = await response.Content.ReadFromJsonAsync<ApiError>(json);
    throw new InvalidOperationException(failure?.Error.Message);
}
var account = await response.Content.ReadFromJsonAsync<Account>(json);
Console.WriteLine($"{account!.Id} {account.ActivationStatus}");

record Account(string Id, string BusinessName, string ActivationStatus, string ApiVersion);
record ApiError(ApiErrorBody Error);
record ApiErrorBody(string Code, string Message);
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }
//             serde = { version = "1", features = ["derive"] }
use serde::Deserialize;

#[derive(Deserialize)]
struct Account {
    id: String,
    activation_status: String,
}

#[derive(Deserialize)]
struct ApiError {
    error: ErrorBody,
}

#[derive(Deserialize)]
struct ErrorBody {
    message: String,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let response = reqwest::blocking::Client::new()
        .get("https://pay-api.adpromax.com/v1/account")
        .bearer_auth(std::env::var("APX_SECRET_KEY")?)
        .send()?;
    if !response.status().is_success() {
        let failure: ApiError = response.json()?;
        return Err(failure.error.message.into());
    }
    let account: Account = response.json()?;
    println!("{} {}", account.id, account.activation_status);
    Ok(())
}
Response200 OK
{
  "id": "org_01k7cz9q3m8v6t2x4b5n7p1r0s",
  "object": "account",
  "business_name": "Atlas Shop",
  "country": "MA",
  "default_currency": "mad",
  "activation_status": "sandbox",
  "capabilities": {
    "card_payments": "inactive",
    "cash_payments": "inactive"
  },
  "api_version": "2026-11-01",
  "created": 1790847000
}

When a key is refused

The API answers 401 or 403 with an error object; its code says why.

StatuscodeWhat happened
401api_key_missingThe request carried no key.
401api_key_invalidThe key does not exist. Check for a missing character.
401api_key_revokedThe key was revoked. Use another one.
401api_key_expiredThe key was rolled and its overlap has ended. Use the new key.
403secret_key_requiredA publishable key was used where a secret key is needed.
403account_inactiveThe account is suspended or closed.
403live_mode_not_activatedA live key was used before the account was activated.

The error's message explains it in the language of your request, and its request_id identifies the request if you write to support.

Keep secret keys secret

  • Read the key from an environment variable or a secret store; never commit it to a repository.
  • Never put a secret key in a web page, a mobile app or a support ticket.
  • If a secret key may have leaked, revoke it on the dashboard and create a new one.

Rolling, revoking and the rest: Security.