# 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:

YAML
    
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.

**API keys**

To start accepting payments, even in the sandbox environment, you require credentials. These credentials are the Public `publicKey` and Secret `secretKey` keys. Apply these keys 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.


Use the Public and Secret keys 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.


**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.

**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 `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
**Steps**

Use the 
generateSignature
function, which takes the data and the Secret key as parameters.
Generate the HMAC-SHA512 hash using the Secret key and data.
Get the hexadecimal representation of the hash.
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.

**Base URLs**

Host
Domain
https://pay.solidgate.com/api/v1
Card payments
https://gate.solidgate.com/api/v1
Alternative payment methods
https://subscriptions.solidgate.com/api/v1
Subscriptions
https://payment-page.solidgate.com/api/v1
Payment Page
https://reports.solidgate.com/api/v1
Reports
https://risks.solidgate.com/api/v1
Fraud prevention
https://api.solidgate.com/api/v1/
Webhooks
and 
Files
**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
```

**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** (Web Application Firewall) 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 field constraints that are not valid.
401
Unauthorized
Merchant or signature headers that are not valid or missing.
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.
**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.

**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 might also differ by endpoint based on operational and reliability needs. System performance is continuously monitored, and limits might be adjusted 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.

**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

Webhook event security works like API authentication. Each notification includes `merchant` and `signature` values in the headers.

- **`merchant`:** your webhook Public key (`wh_pk_`).
- **`signature`:** an HMAC-SHA512 hash of the request body, signed with your webhook Secret key.


**Verify a signature**

Retrieve the 
merchant
value from the request headers and confirm it matches one of your webhook Public keys.
Look up the Secret key paired with that Public key.
Generate a signature from the raw request body using the same 
generateSignature
function you use for API requests.
Compare your generated signature to the 
signature
header value. Reject the request if they do not match.
> Use the raw JSON body exactly as received, with no changes. Serializers, URL encoding, or reformatting can change the byte structure and produce a different hash, causing a valid webhook to fail verification.


.sg-prompt-accordion > div > :first-child { margin-top: 0; }
    .sg-prompt-accordion .sg-prompt-chevron { display:inline-block; transition:transform .15s; }
    .sg-prompt-accordion[open] .sg-prompt-chevron { transform: rotate(90deg); }
    .sg-event-accordion > div { padding: 8px; box-sizing: border-box; }
    .sg-event-accordion .md-table-wrapper,
    .sg-event-accordion table { margin: 0; padding: 0; width: 100%; max-width: 100%; box-sizing: border-box; }
    .sg-event-accordion blockquote { margin: 8px 0 0; }
    
▸
AI prompt to verify v1 webhook signatures
Copy
Implement Solidgate v1 webhook signature verification for my integration.

Context: Solidgate signs v1 webhooks the same way it signs v1 API requests. Every v1 webhook delivery includes five headers: `merchant` (my webhook public key, prefixed `wh_pk_`), `signature` (the value to check), `solidgate-event-id` (a content-derived unique ID, for deduplication), `solidgate-event-created-at` (for replay checks and ordering), and `solidgate-event-type`.

Algorithm to implement:

1. Read the raw request body as bytes, before any JSON parsing or reserialization. The exact byte sequence affects the hash.
2. Confirm the `merchant` header matches my known webhook public key (`wh_pk_...`). Reject immediately if it does not.
3. Build this string: `publicKey + rawBody + publicKey`. The public key wraps both sides of the raw body.
4. Compute HMAC-SHA512 of that string using my webhook secret key (`wh_sk_...`).
5. Hex-encode the raw digest bytes to get a hex string.
6. Base64-encode that hex string, not the raw digest bytes. This double-encoding step is easy to miss. It is the most common cause of "signature never matches" bugs in this integration.
7. Compare the result to the `signature` header using a constant-time comparison. Use `hash_equals` in PHP, `hmac.compare_digest` in Python, `crypto.timingSafeEqual` in Node.js, `hmac.Equal` in Go, `MessageDigest.isEqual` in Java or Kotlin, or `CryptographicOperations.FixedTimeEquals` in C#. Do not use `==` or `.equals()`.
8. Reject the request without processing it if either check fails.


Also implement:

- Idempotency: deduplicate using the `solidgate-event-id` header. Store processed event IDs for at least one week (7 days) and skip duplicates seen within that window.
- Delivery order: do not assume ordering. Delivery order is not guaranteed. Use the `solidgate-event-created-at` header to sequence events on a best-effort basis if needed, but treat it as a hint, not a guarantee.
- Retries: Solidgate retries failed deliveries at these exact intervals after the first attempt: 15 minutes, 30 minutes, 1 hour, 2 hours, 4 hours, 8 hours, 16 hours, then 24 hours (8 attempts total, spanning about 2 days). The same event can arrive more than once because of these retries.
- Fast response: respond with a 2xx status within 30 seconds of receipt. If processing takes longer, verify the signature, durably store the event, return 2xx immediately, then process the event asynchronously in a background job or queue.


After generating the code, list each requirement above (headers, signature algorithm, idempotency, delivery order, retries, fast response) and state how the implementation satisfies it. If any requirement is not addressed, say so explicitly instead of omitting it.

Write this in my language and framework. Ask me which one if it is not clear from my codebase, and match my project's existing conventions for reading raw request bodies and structuring webhook handlers.

**Delivery headers**

Every v1 webhook delivery includes the following headers.

Header
Type
Description
Example
merchant
string
Your webhook public key (
wh_pk_
). Use this to look up the corresponding secret key on your server.
wh_pk_abc123
signature
string
Base64(Hex(HMAC-SHA512)) of the request body, signed with your webhook secret key.
MjNiYW...
solidgate-event-id
string
Content-derived unique ID for the event. Use this to deduplicate deliveries.
e1765cf7-70f7-4e56-8fb2-bd88744a94d1
solidgate-event-created-at
string
Date and time when the event was created in UTC. Use this to order events chronologically.
2025-06-05T12:34:56.789
solidgate-event-type
string
The type of event that occurred.
card_gate.order.updated
**Best practices**

- **Process asynchronously:** Verify the signature, durably store the event, then return a `200 OK` immediately, and handle the payload in a background queue. This avoids timeout failures during traffic spikes.
- **Ensure idempotency:** Delivery order is not guaranteed and the same event might arrive more than once with the same `solidgate-event-id`, including retries spanning up to 24 hours after the first attempt. Store processed event IDs for at least one week and skip any duplicate.


## Webhook events

Subscribe to event types when you create or update a webhook endpoint. Full payload contracts stay on each event page in this reference. For endpoint setup, retries, and delivery headers, see the webhooks guide.

▸
Card payments
Event type
Description
card_gate.order.updated
Real-time status changes for an 
Updated card order
.
card_gate.chargeback.received
Received dispute
notifications for a card chargeback.
card.network_token.created
Created network token
from Visa Token Service, Mastercard Digital Enablement Service, or Secure Card on File.
card.network_token.updated
Status changes for an 
Updated network token
.
card_gate.prevention_alert.received
Issuer 
Received prevention alert
for a potential card chargeback.
card_gate.fraud_alert.received
TC40 or SAFE 
Received fraud alert
from the card network.
> `card_gate.prevention_alert.received` is delivered for both card and PayPal prevention alerts. Inspect the payload to tell the two cases apart.


▸
Alternative payments
Event type
Description
alt_gate.order.updated
Status updates for an 
Updated alternative order
.
alt_gate.paypal_dispute.received
Creation and progression of a 
Received PayPal dispute
.
card_gate.prevention_alert.received
Issuer 
Received prevention alert
for a potential PayPal chargeback. The token is the same as for card prevention alerts.
alt_gate.recurring_token.cancelled
A 
Revoked recurring token
for an alternative payment method.
▸
Subscriptions
Event type
Description
subscription.updated.v2
Subscription initiation and status changes in 
Subscription payment updates
and the 
subscription events
guide.
▸
Taxes
Event type
Description
taxer.tax.calculated
Tax calculation results for a 
Calculated tax
transaction.
## 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:

Go to 
Developers
> 
API logs
.
Find the log entry you need and select it.
View log entry details, including 
Status
, 
Method
, 
Path
, 
Host
, 
IP address
, and 
Date (UTC)
.
Expand 
Request body
and 
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 might be required.

> Any API change that requires client-side adjustments is communicated in advance, with 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.


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.