Create Payment via API
Learn how to create cryptocurrency payments using the HexPay REST API
Before creating payments via API, make sure you have:
API Endpoint
POST https://api.hexpay.io/v1/paymentsOptionally pass an X-Idempotency-Key header (UUID) to prevent duplicate payments on retry.
Two Modes of Payment Creation
1. Customer Choice (Recommended)
The customer sees all payment methods enabled for your store and picks one at checkout. Use this when you want to give the customer flexibility.
The payment is created in created status. paymentDetails (deposit address and coin amount) is absent until the customer selects a method.
curl -X POST "https://api.hexpay.io/v1/payments" \
-H "Authorization: Bearer eyJhbGciOiJFZERTQSIsImtpZCI6IjFjZDZhOTIy..." \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" \
-d '{
"amount": "100.00",
"currency": "USD",
"order_id": "order-2026-00123",
"webhookURL": "https://merchant.com/webhooks/hexpay"
}'const response = await fetch("https://api.hexpay.io/v1/payments", {
method: "POST",
headers: {
"Authorization": "Bearer eyJhbGciOiJFZERTQSIsImtpZCI6IjFjZDZhOTIy...",
"Content-Type": "application/json",
"X-Idempotency-Key": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
},
body: JSON.stringify({
amount: "100.00",
currency: "USD",
order_id: "order-2026-00123",
webhookURL: "https://merchant.com/webhooks/hexpay"
})
});
const payment = await response.json();package main
import (
"fmt"
"net/http"
"io/ioutil"
"strings"
)
func main() {
body := strings.NewReader(`{
"amount": "100.00",
"currency": "USD",
"order_id": "order-2026-00123",
"webhookURL": "https://merchant.com/webhooks/hexpay"
}`)
req, _ := http.NewRequest("POST", "https://api.hexpay.io/v1/payments", body)
req.Header.Add("Authorization", "Bearer eyJhbGciOiJFZERTQSIsImtpZCI6IjFjZDZhOTIy...")
req.Header.Add("Content-Type", "application/json")
req.Header.Add("X-Idempotency-Key", "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
respBody, _ := ioutil.ReadAll(res.Body)
fmt.Println(string(respBody))
}import requests
response = requests.post(
"https://api.hexpay.io/v1/payments",
json={
"amount": "100.00",
"currency": "USD",
"order_id": "order-2026-00123",
"webhookURL": "https://merchant.com/webhooks/hexpay"
},
headers={
"Authorization": "Bearer eyJhbGciOiJFZERTQSIsImtpZCI6IjFjZDZhOTIy...",
"Content-Type": "application/json",
"X-Idempotency-Key": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
}
)
payment = response.json()Response — payment in created status:
{
"id": "019327c6-2058-7901-b234-56789abcdeff",
"status": "created",
"order_id": "order-2026-00123",
"amount": "100.00",
"currency": "USD",
"checkoutURL": "https://payment.hexpay.io/019327c6-2058-7901-b234-56789abcdeff",
"timer": {
"createdAt": "2026-01-20T10:00:00Z",
"expiresAt": "2026-01-20T11:00:00Z"
}
}Redirect the customer to checkoutURL — they will see all available payment methods and pick one. paymentDetails appears once they select a method and the payment moves to pending.
2. Preselected Method
You specify the exact coin and chain upfront. The server resolves the deposit address immediately — the customer skips method selection and goes straight to the deposit screen.
The payment is created directly in pending status with paymentDetails already populated.
Use coin and chain symbol values from GET /v1/payment-methods. Values are case-sensitive.
curl -X POST "https://api.hexpay.io/v1/payments" \
-H "Authorization: Bearer eyJhbGciOiJFZERTQSIsImtpZCI6IjFjZDZhOTIy..." \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" \
-d '{
"amount": "100.00",
"currency": "USD",
"payment_options": {
"methods": [{ "coin": "USDT", "chain": "TON" }]
},
"order_id": "order-2026-00124",
"webhookURL": "https://merchant.com/webhooks/hexpay"
}'const response = await fetch("https://api.hexpay.io/v1/payments", {
method: "POST",
headers: {
"Authorization": "Bearer eyJhbGciOiJFZERTQSIsImtpZCI6IjFjZDZhOTIy...",
"Content-Type": "application/json",
"X-Idempotency-Key": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
},
body: JSON.stringify({
amount: "100.00",
currency: "USD",
payment_options: {
methods: [{ coin: "USDT", chain: "TON" }]
},
order_id: "order-2026-00124",
webhookURL: "https://merchant.com/webhooks/hexpay"
})
});
const payment = await response.json();package main
import (
"fmt"
"net/http"
"io/ioutil"
"strings"
)
func main() {
body := strings.NewReader(`{
"amount": "100.00",
"currency": "USD",
"payment_options": {
"methods": [{ "coin": "USDT", "chain": "TON" }]
},
"order_id": "order-2026-00124",
"webhookURL": "https://merchant.com/webhooks/hexpay"
}`)
req, _ := http.NewRequest("POST", "https://api.hexpay.io/v1/payments", body)
req.Header.Add("Authorization", "Bearer eyJhbGciOiJFZERTQSIsImtpZCI6IjFjZDZhOTIy...")
req.Header.Add("Content-Type", "application/json")
req.Header.Add("X-Idempotency-Key", "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
respBody, _ := ioutil.ReadAll(res.Body)
fmt.Println(string(respBody))
}import requests
response = requests.post(
"https://api.hexpay.io/v1/payments",
json={
"amount": "100.00",
"currency": "USD",
"payment_options": {
"methods": [{"coin": "USDT", "chain": "TON"}]
},
"order_id": "order-2026-00124",
"webhookURL": "https://merchant.com/webhooks/hexpay"
},
headers={
"Authorization": "Bearer eyJhbGciOiJFZERTQSIsImtpZCI6IjFjZDZhOTIy...",
"Content-Type": "application/json",
"X-Idempotency-Key": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
}
)
payment = response.json()Response — payment immediately in pending status with deposit address:
{
"id": "019327c6-2058-7901-b234-56789abcdeff",
"status": "pending",
"order_id": "order-2026-00124",
"amount": "100.00",
"currency": "USD",
"checkoutURL": "https://payment.hexpay.io/019327c6-2058-7901-b234-56789abcdeff",
"paymentDetails": {
"address": "UQBf_DO8wsBddOcMnGEOyREEHPPKMBL2F26GQFOV7FQdYY_a",
"coinAmount": "35.782",
"coin": {
"name": "Tether",
"symbol": "USDT",
"imgUrl": "https://assets.hexpay.io/coins/usdt.svg"
},
"chain": {
"name": "TON",
"symbol": "TON",
"imgUrl": "https://assets.hexpay.io/chains/ton.svg"
}
},
"timer": {
"createdAt": "2026-01-20T10:00:00Z",
"expiresAt": "2026-01-20T11:00:00Z"
}
}You can either redirect the customer to checkoutURL (they land directly on the deposit screen) or display paymentDetails.address and paymentDetails.coinAmount in your own UI — they must send exactly that amount to that address on the TON blockchain.
Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
amount | string | Yes | Positive decimal, e.g. "100.00". No leading zeros on the integer part. |
currency | string | Yes | Fiat code (USD, EUR, RUB) or coin ticker (TON, USDT, NOT, DOGS, BUILD, BLUM, DUST, DYOR, STON, XAUt0). Defines the unit of value, not the payment coin. |
payment_options.methods | array | No | Exactly one {coin, chain} entry for Preselected mode. Omit for Customer Choice. |
ttl | integer (int64) | No | Payment window in seconds. Min 900, max 43200, default 3600. |
order_id | string | No | Your internal order ID, max 128 chars. Returned in all responses and webhooks. |
description | string | No | Human-readable payment purpose, max 512 chars. May be shown to the customer. |
metadata | object | No | Arbitrary key-value pairs (max 10 keys, string values up to 512 chars). |
webhookURL | string | No | HTTPS URL for lifecycle notifications. Must be publicly reachable — localhost and private network addresses are rejected. |
X-Idempotency-Key | string (UUID) | No | Header. If provided, retrying with the same key within 24 hours returns the cached response without creating a duplicate payment. |
Supported currency + method combinations
currency | payment_options.methods | Initial status | paymentDetails |
|---|---|---|---|
USD | (omitted) | created | absent |
TON | (omitted) | created | absent |
USD | [{coin:"USDT",chain:"TON"}] | pending | populated |
TON | [{coin:"USDT",chain:"TON"}] | pending | populated |
TON | [{coin:"TON",chain:"TON"}] | pending | populated |
Tracking Payment Status
After creating a payment, poll its status to know when to fulfill the order.
For lightweight polling use GET /v1/payments/{id}/status — it returns only the status field. Once the status you care about is reached, fetch the full object with GET /v1/payments/{id}.
created → pending → completed ✓ fulfill order
↘ ↘
cancelled expiredConfigure webhookURL when creating the payment to receive a POST notification when the payment is completed — this removes the need to poll at all.