# Changelog

Every version of the AdProMaxPay API, what it holds, and whether it breaks existing code.

This page lists every version of the API, newest first. Each entry says whether it is **breaking**:
whether code written for the version before it must change.

## How versions work

* A version is named by its date: `2026-11-01`.
* Your organization is **pinned** to the version that was current when it was created. Its
  `api_version`, in `GET /v1/account`, names it. Your requests use it unless they say otherwise.
* A request can name a version with the `X-AdProMaxPay-Version` header. An unknown version is refused
  with `400`, `api_version_invalid`.
* Every event carries the `api_version` it was written with.

A **breaking change** comes in a new dated version, and the version you are pinned to keeps working
as it did. **Additions** come without a new version: new objects, new fields, new event types, new
optional parameters and new error codes. Write your code to ignore the fields and the event types it
does not know.

```bash
curl https://pay-api.adpromax.com/v1/account \
  -u "$APX_SECRET_KEY:" \
  -H "X-AdProMaxPay-Version: 2026-11-01"
```

```js title="version.mjs"
const response = await fetch('https://pay-api.adpromax.com/v1/account', {
  headers: {
    'Authorization': `Bearer ${process.env.APX_SECRET_KEY}`,
    'X-AdProMaxPay-Version': '2026-11-01',
  },
})
const account = await response.json()
console.log(account.api_version)
```

```ts title="version.ts"
interface Account {
  id: string
  object: 'account'
  api_version: string
}

const response = await fetch('https://pay-api.adpromax.com/v1/account', {
  headers: {
    'Authorization': `Bearer ${process.env.APX_SECRET_KEY}`,
    'X-AdProMaxPay-Version': '2026-11-01',
  },
})
const account = (await response.json()) as Account
console.log(account.api_version)
```

```python title="version.py"
import os

import requests

response = requests.get(
    "https://pay-api.adpromax.com/v1/account",
    auth=(os.environ["APX_SECRET_KEY"], ""),
    headers={"X-AdProMaxPay-Version": "2026-11-01"},
)
account = response.json()
print(account["api_version"])
```

```php title="version.php"
<?php
$ch = curl_init('https://pay-api.adpromax.com/v1/account');
curl_setopt_array($ch, [
    CURLOPT_USERPWD => getenv('APX_SECRET_KEY') . ':',
    CURLOPT_HTTPHEADER => ['X-AdProMaxPay-Version: 2026-11-01'],
    CURLOPT_RETURNTRANSFER => true,
]);
$account = json_decode(curl_exec($ch), true);
echo $account['api_version'], PHP_EOL;
```

```ruby title="version.rb"
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'), '')
request['X-AdProMaxPay-Version'] = '2026-11-01'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
  http.request(request)
end
account = JSON.parse(response.body)
puts account['api_version']
```

```go title="version.go"
package main

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

type Account struct {
	ID         string `json:"id"`
	APIVersion string `json:"api_version"`
}

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"), "")
	req.Header.Set("X-AdProMaxPay-Version", "2026-11-01")

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

	var account Account
	if err := json.NewDecoder(res.Body).Decode(&account); err != nil {
		log.Fatal(err)
	}
	fmt.Println(account.APIVersion)
}
```

```java title="Version.java"
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Version {
    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"))
            .header("X-AdProMaxPay-Version", "2026-11-01")
            .GET()
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        // The JDK has no JSON parser: a small regex reads api_version.
        Matcher version = Pattern
            .compile("\"api_version\"\\s*:\\s*\"([^\"]*)\"")
            .matcher(response.body());
        System.out.println(version.find() ? version.group(1) : response.body());
    }
}
```

```csharp title="Version.cs"
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"));
http.DefaultRequestHeaders.Add("X-AdProMaxPay-Version", "2026-11-01");

var account = await http.GetFromJsonAsync<Account>("https://pay-api.adpromax.com/v1/account", json);
Console.WriteLine(account!.ApiVersion);

record Account(string Id, string ApiVersion);
```

```rust title="main.rs"
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }
//             serde = { version = "1", features = ["derive"] }
use serde::Deserialize;

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

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let account: Account = reqwest::blocking::Client::new()
        .get("https://pay-api.adpromax.com/v1/account")
        .bearer_auth(std::env::var("APX_SECRET_KEY")?)
        .header("X-AdProMaxPay-Version", "2026-11-01")
        .send()?
        .json()?;
    println!("{}", account.api_version);
    Ok(())
}
```

## 2026-11-01

**Not breaking**: the first version of the API. It holds:

* **The account**: `GET /v1/account`, your organization and its activation status. See
  [Your API keys](https://pay-docs.adpromax.com/en/get-started/api-keys).
* **Payment intents and charges**, by card and in cash, with their statuses and attempts. See
  [How payments work](https://pay-docs.adpromax.com/en/payments/how-payments-work).
* **Checkout sessions**, for the hosted payment page. See [Hosted checkout](https://pay-docs.adpromax.com/en/payments/hosted-checkout).
* **Customers**. See [Customers](https://pay-docs.adpromax.com/en/payments/customers).
* **Refunds** of card payments. See [Refunds](https://pay-docs.adpromax.com/en/payments/refunds).
* **Events and webhook endpoints**: signed deliveries, retried for about three days. See
  [Webhooks](https://pay-docs.adpromax.com/en/webhooks).
* **Test helpers**, in test mode only. See [Test helpers API](https://pay-docs.adpromax.com/en/sandbox/test-helpers).
* **Idempotency**: required on the requests that move money, answers kept 24 hours. See
  [Idempotency](https://pay-docs.adpromax.com/en/api-reference/idempotency).
* **Errors** in seven types, with messages in French, English or Arabic, from the `Accept-Language`
  header. See [Handling errors](https://pay-docs.adpromax.com/en/errors).
* **Cursor pagination** on lists: `limit`, `starting_after` and `ending_before`.
* **Rate limits** per organization and mode, with the `RateLimit-*` headers. See
  [Rate limits](https://pay-docs.adpromax.com/en/api-reference/rate-limits).
* **Metadata** on payment intents, checkout sessions, customers, refunds and webhook endpoints. See
  [Metadata](https://pay-docs.adpromax.com/en/payments/metadata).
