# Get started

Learn about authentication, signature creation, error handling, and API key management.

## OpenAPI specification

The complete API schema is available as a single bundled file. It includes all endpoints, request and response structures, and field definitions. To integrate programmatically, download the specification in the preferred format, instead of parsing the rendered API reference:

div
a
svg
rect
path
YAML
    
a
svg
path
path
JSON
    
## Authentication and security

The Solidgate API v1 uses a signature-based authentication model. Each request must carry two headers that are computed from your Public and Secret key pair.

br
**API keys**

To start accepting payments, even in the sandbox environment, you require credentials. These credentials are the Public `publicKey` and Secret `secretKey` keys, which should be applied for direct API calls and to check the webhook signature.

- **Format:** Public keys use the prefix `api_pk_`, secret keys use the prefix `api_sk_`.
- **Source:** Get both keys from the Solidgate Hub by navigating to **Developers** > **Channel details**.
- **Webhook keys** are a separate pair with the prefixes `wh_pk_` / `wh_sk_`, used exclusively for validating webhook payloads.


The Public and Secret keys are applied to calculate the signature, verifying both the source and the integrity of the request details transmitted between the merchant and gateway.

> Never expose your Secret key in client-side code or public repositories. Use it only in secure server-side environments.


br
**Required headers**

Every request must include two custom headers.

| Header | Description | Example |
|  --- | --- | --- |
| `merchant` | Your Public key, used to identify the requesting merchant. | `api_pk_7b197...ba108f842` |
| `signature` | HMAC-SHA512 signature that verifies the request source and data integrity. | `MjNiYFdSdjVj...hYmNiZDY` |


Solidgate uses a similar authentication method for webhooks, with merchant and signature parameters included in the headers.

If you receive an incorrect signature response, verify your API keys and encryption value, then contact support for further assistance.

**Fail response authentication**

```json
{
  "error": {
    "code": "1.01",
    "messages": [
      "Authentication failed"
    ]
  }
}
```

## Generate signature

The signature value is a Base64-encoded string of the hexadecimal representation of an HMAC-SHA512 hash, created using the Secret key.

br
**Signature data**

Concatenate the following string and use it as the HMAC input `publicKey` + `jsonString` + `publicKey`

> For **GET** requests that do not have a body, the signature data is simply `publicKey` + `publicKey`.


| Field | Description | Test data |
|  --- | --- | --- |
| `publicKey` | Your Public key. | `api_pk_8f8a8k8e8k8e8y8` |
| `jsonString` | Request body as a JSON string. | `{"amount": "100", "currency": "USD"}` |
| `secretKey` | Your Secret key. | `api_sk_8f8a8k8e8k8e8y8` |


br
**Steps**

ol
li
Use the 
code
generateSignature
function, which takes the data and the Secret key as parameters.
li
Generate the HMAC-SHA512 hash using the Secret key and data.
li
Get the hexadecimal representation of the hash.
li
Encode the hexadecimal representation of the hash directly to Base64.
**Expected signature with the test data above**

```text
MjFkZGE3ZTZjODc0YjY5YTczOTlmOTBlYjk0MDY1NThiODJiZmE3ZTgxOGJjMWUxYjNkNTFjMDNjZmUzOGRlMTBhZGEzMmYxMGY3NTBlOTBlMGZkNDUwZTRiNmI5YTBiYTVmZWM5NzcxMjU3OWM0MGU5Mzg1NTljOTE1NTVlNzA=
```

**Code examples**

```php
class SignatureGenerator
{
        public static function generateSignature(string $publicKey, string $jsonString, string $secret_key): string
    {
        $text = $publicKey . $jsonString . $publicKey;
        $hashedBytes = hash_hmac('sha512', $text, $secret_key);
        return base64_encode($hashedBytes);
    }
    public static function main(array $args): void
    {
        $public_key = 'api_pk_8f8a8k8e8k8e8y8';
        $json_string = '{"amount": "100", "currency": "USD"}';
        $secret_key = 'api_sk_8f8a8k8e8k8e8y8';
        $signature = self::generateSignature($public_key, $json_string, $secret_key);
        echo $signature . "\n";
    }
}

SignatureGenerator::main([]);
```

```js
const crypto = require('crypto');

function generateSignature(publicKey, jsonString, secretKey) {
  const data = publicKey + jsonString + publicKey;
  const hmac = crypto.createHmac('sha512', secretKey);
  hmac.update(data);
  const hash = hmac.digest();
  const hexHash = hash.toString('hex');
  return Buffer.from(hexHash).toString('base64');
}

const publicKey = 'api_pk_8f8a8k8e8k8e8y8';
const jsonString = '{"amount": "100", "currency": "USD"}';
const secretKey = 'api_sk_8f8a8k8e8k8e8y8';

const signature = generateSignature(publicKey, jsonString, secretKey);
console.log(signature);
```

```python
# This code is intended for Python 3 or newer.
import hashlib
import hmac
import base64

def generateSignature(public_key, json_string, secret_key):
    data = public_key + json_string + public_key
    hmac_hash = hmac.new(secret_key.encode('utf-8'), data.encode('utf-8'), hashlib.sha512).digest()
    return base64.b64encode(hmac_hash.hex().encode('utf-8')).decode('utf-8')

public_key = "api_pk_8f8a8k8e8k8e8y8"
json_string = "{\"amount\": \"100\", \"currency\": \"USD\"}"
secret_key = "api_sk_8f8a8k8e8k8e8y8"

signature = generateSignature(public_key, json_string, secret_key)
print(signature)
```

```go
package main

import (
	"crypto/hmac"
	"crypto/sha512"
	"encoding/base64"
	"encoding/hex"
	"fmt"
)

func generateSignature(publicKey, jsonString, secretKey string) string {
	data := publicKey + jsonString + publicKey
	mac := hmac.New(sha512.New, []byte(secretKey))
	mac.Write([]byte(data))
	hexHash := hex.EncodeToString(mac.Sum(nil))
	return base64.StdEncoding.EncodeToString([]byte(hexHash))
}

func main() {
	publicKey := "api_pk_8f8a8k8e8k8e8y8"
	jsonString := `{"amount": "100", "currency": "USD"}`
	secretKey := "api_sk_8f8a8k8e8k8e8y8"

	signature := generateSignature(publicKey, jsonString, secretKey)
	fmt.Println(signature)
}
```

```java
//dependency - https://mvnrepository.com/artifact/com.google.guava/guava
import com.google.common.hash.Hashing;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class SignatureGenerator {

  public static String generateSignature(String publicKey, String jsonString, String secretKey) {
    String text = publicKey + jsonString + publicKey;
    byte[] hashedBytes = Hashing.hmacSha512(secretKey.getBytes())
        .hashString(text, StandardCharsets.UTF_8).toString().getBytes();
    return Base64.getEncoder().encodeToString(hashedBytes);
  }

  public static void main(String[] args) {
    String publicKey = "api_pk_8f8a8k8e8k8e8y8";
    String jsonString = "{\"amount\": \"100\", \"currency\": \"USD\"}";
    String secretKey = "api_sk_8f8a8k8e8k8e8y8";
    String signature = generateSignature(publicKey, jsonString, secretKey);
    System.out.println(signature);
  }

}
```

```csharp
using System;
using System.Security.Cryptography;
using System.Text;

public class SignatureGenerator {

    public static string GenerateSignature(string publicKey, string jsonString, string secretKey) {
        string data = publicKey + jsonString + publicKey;
        byte[] secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
        byte[] dataBytes = Encoding.UTF8.GetBytes(data);
        using (var hmac = new HMACSHA512(secretKeyBytes)) {
            byte[] hashBytes = hmac.ComputeHash(dataBytes);
            string hexHash = BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
            byte[] base64Bytes = Encoding.UTF8.GetBytes(hexHash);
            string base64Signature = Convert.ToBase64String(base64Bytes);
            return base64Signature;
        }
    }

    public static void Main(string[] args) {
        string publicKey = "api_pk_8f8a8k8e8k8e8y8";
        string jsonString = "{\"amount\": \"100\", \"currency\": \"USD\"}";
        string secretKey = "api_sk_8f8a8k8e8k8e8y8";
        string signature = SignatureGenerator.GenerateSignature(publicKey, jsonString, secretKey);
        Console.WriteLine(signature);
    }
}
```

```kotlin
package com.solidgate.examples

// https://mvnrepository.com/artifact/com.google.guava/guava
import com.google.common.hash.Hashing
import java.nio.charset.StandardCharsets
import java.util.Base64

class SignatureGenerator {
    companion object {
        fun generateSignature(publicKey: String, jsonString: String, secretKey: String): String {
            val text = publicKey + jsonString + publicKey
            val hashedBytes = Hashing.hmacSha512(secretKey.toByteArray())
                .hashString(text, StandardCharsets.UTF_8).toString().toByteArray()
            return Base64.getEncoder().encodeToString(hashedBytes)
        }
    }
}

fun main() {
    val publicKey = "api_pk_8f8a8k8e8k8e8y8"
    val jsonString = "{\"amount\": \"100\", \"currency\": \"USD\"}"
    val secretKey = "api_sk_8f8a8k8e8k8e8y8"
    val signature = SignatureGenerator.generateSignature(publicKey, jsonString, secretKey)
    println(signature)
}
```

```ruby
require 'openssl'
require 'base64'

def generate_signature(public_key, json_string, secret_key)
  digest = OpenSSL::Digest.new('sha512')
  instance = OpenSSL::HMAC.new(secret_key, digest)
  instance.update(public_key + json_string + public_key)
  Base64.strict_encode64(instance.hexdigest)
end

# Example usage
public_key = "api_pk_8f8a8k8e8k8e8y8"
json_string = '{"amount": "100", "currency": "USD"}'
secret_key = "api_sk_8f8a8k8e8k8e8y8"

signature = generate_signature(public_key, json_string, secret_key)
puts "Signature value: #{signature}"
```

## API structure and conventions

The v1 API uses domain-specific base URLs, except for endpoints for managing webhooks and files. Select the correct host for the resource you are working with.

br
**Base URLs**

| Host | Domain |
|  --- | --- |
| `https://pay.solidgate.com/api/v1` | **[Card payments](https://api-docs.solidgate.com/api/card-payments)** |
| `https://gate.solidgate.com/api/v1` | **[Alternative payment methods](https://api-docs.solidgate.com/api/alternative-payment-methods)** |
| `https://subscriptions.solidgate.com/api/v1` | **[Subscriptions](https://api-docs.solidgate.com/api/manage-subscriptions)** |
| `https://payment-page.solidgate.com/api/v1` | **[Payment Page](https://api-docs.solidgate.com/api/payment-page)** |
| `https://reports.solidgate.com/api/v1` | **[Reports](https://api-docs.solidgate.com/api/reports)** |
| `https://risks.solidgate.com/api/v1` | **[Fraud prevention](https://api-docs.solidgate.com/api/fraud-prevention)** |
| `https://api.solidgate.com/api/v1/` | **[Webhooks](https://api-docs.solidgate.com/api/manage-webhooks)** and **[Files](https://api-docs.solidgate.com/api/files-upload)** |


**Endpoint format**

Endpoints use the action name directly after the base URL. The HTTP method depends on the operation.

```text
{method} {host}/{action}
```

For example, to initiate a card payment:

```bash
POST https://pay.solidgate.com/api/v1/charge
```

br
**Data formatting**

- **Body:** JSON
- **Content-Type:** `application/json`


For the complete list of endpoints, methods, and parameters, see the Solidgate API reference.

> Using an action with the wrong base URL results in a **Blocked by WAF** error. For example, the endpoint `/subscription/cancel-by-customer` must be called on `subscriptions.solidgate.com`, not `pay.solidgate.com`.


## Error handling

The API uses standard HTTP status codes. All errors return a consistent JSON structure.

| Status | Category | Description |
|  --- | --- | --- |
| 400 | Validation | Malformed JSON or invalid field constraints |
| 401 | Unauthorized | Invalid or missing merchant/signature headers |
| 403 | Access denied | Missing required permissions |
| 404 | Not found | Resource or endpoint does not exist |
| 422 | State error | Request conflicts with current system state |
| 429 | Rate limit | Request quota exhausted |
| 500 | Internal | Server-side failure |


br
**Error response structure**

```json
{
  "error": {
    "code": "3.02",
    "messages": [
      "The user's card balance has insufficient funds."
    ],
    "recommended_message_for_user": "Please try a different payment method."
  }
}
```

For the full list of error codes, see the error codes reference.

## Rate limits

Rate limiting controls the frequency at which requests are made to API endpoints within specific time periods.

It helps protect against service overload while ensuring consistent performance for all clients. Exceeding limits results in a `429 Too many requests` error response.

br
**API usage limits**

Solidgate returns the `429` error response when necessary to protect legitimate merchant traffic.

Rate limits apply per merchant account and differ between environments. In the Live environment, the limit is 25 requests per second, distributed per merchant account. In the Sandbox test environment, the limit is 10 requests per second, across all endpoints per merchant account. When the rate limit is exceeded, the API returns `HTTP 429` with error code 5.07.

Rate limits may also differ by endpoint based on operational and reliability needs. The Solidgate team continuously monitors system performance and may adjust these limits as needed to maintain optimal service quality.

For endpoint-specific rate limit information, visit the **Developers** section in the Solidgate Hub, which is updated as changes occur.

br
**Handle rate limits**

You can handle rate limiting by monitoring for the `429 Too many requests` error response. Effective handling combines retries and overall request flow control.

A widely used approach for handling rate limit error responses is implementing exponential backoff with jitter. This method retries requests using short initial delays that increase after each failure. Introducing randomization, or jitter, helps avoid conflicts caused by multiple clients retrying simultaneously.

While retries are useful, a significant improvement comes from regulating request flow across the entire application. The token bucket algorithm is standard practice for this purpose. It allows short bursts of requests while enforcing an average request rate over time, reducing traffic spikes and improving overall stability. Token bucket implementations exist in many programming languages and can be applied client-side to reduce failed requests and maintain consistent performance.

By following these practices, your application can handle rate limits smoothly, avoid unnecessary request failures, and maintain reliable performance when interacting with the Solidgate APIs.

## Webhook validation

Solidgate signs outgoing webhook payloads with the same HMAC-SHA512 method used for API requests. Use the dedicated Webhook keys (`wh_pk_` / `wh_sk_`) to verify inbound webhooks. For more details, see the webhook security guide.

1. Extract the `merchant` and `signature` values from the webhook headers.
2. Reconstruct the expected signature using the webhook Public key, the raw JSON body, and the webhook Secret key.
3. Compare the computed signature to the one in the header. Reject the request if they do not match.


## Outgoing request IPs

Specific IP addresses are used for outbound requests from Solidgate to your systems (for example, webhooks). Allow traffic from these IPs in your firewall or security configuration to ensure uninterrupted service.

Configure your security systems to accept these IPs to prevent service interruptions. Stay informed about any changes to these IP addresses for continuous service.

| IP addresses |
|  --- |
| `3.74.184.6` / `3.121.136.242` |
| `18.156.25.95` / `18.157.254.13` / `18.157.119.243` / `18.184.24.146` / `18.192.168.222` / `18.195.90.222` |
| `35.157.172.91` / `35.165.202.104` |
| `44.224.79.149` |
| `52.10.37.135` / `52.88.195.65` |


## Handle WAF errors

The **Blocked by WAF** error indicates that a Web Application Firewall (WAF) has prevented your request due to a policy violation. The most common cause is sending a request to the wrong base URL. This error often arises from mismatched endpoints and base URLs in API requests.

1. Confirm that the full URL matches a valid endpoint in the Solidgate API reference.
2. Ensure that the endpoint corresponds to the correct base URL for that domain.


For example, if you attempt to cancel a subscription using the endpoint `/subscription/cancel-by-customer` at the base URL `pay.solidgate.com/api/v1`, you receive a **Blocked by WAF** error. The correct base URL for subscription actions is `subscriptions.solidgate.com/api/v1`.

> The WAF is configured to block IP addresses from sanctioned countries to increase security and ensure compliance with international regulations. To avoid this issue, ensure your IP address is not from a sanctioned country.


## API logs

API logs capture API request and response records for operations.

This helps developers and integrators troubleshoot issues, monitor traffic, and investigate suspicious or unintended API usage.

To view API log entries in the Solidgate Hub:

ol
li
Go to 
strong
Developers
> 
strong
API logs
.
li
Find the log entry you need and click on it.
li
View log entry details, including 
strong
Status
, 
strong
Method
, 
strong
Path
, 
strong
Host
, 
strong
IP address
, and 
strong
Date (UTC)
.
li
Expand 
strong
Request body
and 
strong
Response body
to see the JSON payloads sent and received.
## Backward compatibility

Changes to the Solidgate APIs are aimed at minimizing the impact on existing integrations. However, depending on the type of change, actions may be required.

> Any API change that requires client-side adjustments will be communicated by the Solidgate team. These changes include preparation time and clear guidance.
When a field or feature is marked as **deprecated** and a **deprecation notice** is added to the changelog, it serves as an early notice. Deprecated fields or features are not removed immediately.


br
Breaking changes can impact existing integrations and require adjustments. These are marked with a **breaking changes** badge in the changelog and include:

| Category | Change |
|  --- | --- |
| **Operation removal** | Removing an API operation. |
| **Request** | Remove or rename a field, make optional fields required, remove `oneOf`. |
| **Response** | Remove or rename a field, change HTTP status code, remove `oneOf`. |
| **Type changes** | Change request or response data types. |
| **HTTP headers** | Add required headers or remove existing ones. |
| **Enum updates** | Remove enum values. |
| **Errors** | Change existing error codes. |
| **Validation rules** | Add stricter or new rules. |
| **Authentication and authorization** | Change requirements. |


Non-breaking changes modifications do not affect existing integrations and ensure backward compatibility:

| Category | Change |
|  --- | --- |
| **Request** | Add new optional fields, change required fields to optional. |
| **Response** | Add new optional fields, change optional fields to required. |
| **HTTP headers** | Add new optional headers, change header case. |
| **Field length** | Expand maximum length. |
| **Identifier format** | Change prefixes or formatting. |
| **Webhook events** | Add new opt-in event types. |
| **Webhook schema** | Add new fields. |
| **Rate limiting** | Changes communicated at least one month in advance. |


For help, contact us. Stay informed with the Changelog.