Gramix API v1
Use this guide as the contract for your server-side integration.
General information
The public API lets you check balances, fund your wallet, and purchase Telegram Stars, Telegram Premium, and GRAM.
Base URL
https://api.gramix.io/api/v1Data format
- API request bodies and responses use application/json.
- Monetary values are sent as strings with four decimal places. For accurate calculations, use decimal types or keep the values as strings.
- Webhook timestamps use the ISO 8601 format. Do not rely on unknown JSON fields.
Available methods
| Method | Endpoint | Purpose | HTTP status |
|---|---|---|---|
GET | /api/v1/wallets/balance | Returns the current balance. | 200 |
POST | /api/v1/wallets/balance | Creates or returns the address and memo required to fund the wallet on the TON network. | 201 |
POST | /api/v1/purchase/stars | Purchases between 50 and 1,000,000 Telegram Stars for the specified Telegram username. | 201 |
POST | /api/v1/purchase/premium/{duration} | Purchases Telegram Premium for 3, 6, or 12 months. | 201 |
POST | /api/v1/purchase/gram | Purchases and sends between 1 and 10,000 GRAM to the specified Telegram recipient. | 201 |
Authentication
All methods require an active API key sent in the x-api-key HTTP header.
x-api-key: YOUR_API_KEYExample request
curl --request GET 'https://api.gramix.io/api/v1/wallets/balance' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Accept: application/json'If the header is missing or the key is invalid, revoked, or inactive, the API returns 401 Unauthorized.
Response format
Successful response
A successful response contains statusCode and data. POST requests return HTTP status 201.
{
"statusCode": 200,
"data": {}
}Error response
Errors contain statusCode and a human-readable message. Client logic should primarily rely on the HTTP status code.
{
"statusCode": 400,
"message": "Idempotency-Key header is required"
}Common errors
| HTTP status | message |
|---|---|
400 | Bad Request |
401 | Unauthorized |
403 | Insufficient balance |
404 | Not Found |
405 | Method Not Allowed |
500 | Internal server error |
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"statusCode": 401,
"message": "Unauthorized"
}HTTP/1.1 403 Forbidden
Content-Type: application/json
{
"statusCode": 403,
"message": "Insufficient balance"
}Common schemas
PaymentCurrency
paymentCurrency selects the balance used to pay for the purchase. Only lowercase gram and usdt are accepted.
type PaymentCurrency = "gram" | "usdt";RecipientName
recipientName is the recipient's Telegram username without @. It must be 5 to 32 characters long and may contain lowercase Latin letters, digits, and _.
durov
user_123
telegram_user@durov
User123
1username
user-name
abcPurchaseRequestBase
{
"recipientName": "telegram_user",
"paymentCurrency": "usdt"
}PurchaseResponse
All purchase methods return orderId, status, and idempotencyKey. A new order normally has the processing status.
{
"statusCode": 201,
"data": {
"orderId": "7ba77616-7706-4215-9e6f-b6c67e4df292",
"status": "processing",
"idempotencyKey": "order_20260713_0001"
}
}type OrderStatus = "created" | "processing" | "completed" | "failed" | "canceled";Purchase idempotency
All /purchase/* methods require an idempotency-key header between 8 and 64 characters long.
idempotency-key: UNIQUE_REQUEST_KEY- Latin letters, digits, _, and - are accepted. The key must be unique for the API key.
- After a network error, retry the same request with the same key. Use a new key for a new purchase.
order_20260713_0001
01J2Y5VN7CV8ZQ7DPZ9J0P6E4M
client-req-12345678Possible errors
{
"statusCode": 400,
"message": "Idempotency-Key header is required"
}{
"statusCode": 400,
"message": "Idempotency-Key must be between 8 and 64 characters"
}{
"statusCode": 400,
"message": "Idempotency-Key contains invalid characters"
}Wallets
GET /api/v1/wallets/balance
Returns the current balance.
| Headers | Required | Description |
|---|---|---|
x-api-key | yes | Authentication |
Accept | no | application/json |
Example request
curl --request GET 'https://api.gramix.io/api/v1/wallets/balance' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Accept: application/json'{
"statusCode": 200,
"data": {
"gram": "14.2500",
"usdt": "125.5000"
}
}POST /api/v1/wallets/balance
Creates or returns the address and memo required to fund the wallet on the TON network.
| Headers | Required | Description |
|---|---|---|
x-api-key | yes | Authentication |
Accept | no | application/json |
curl --request POST 'https://api.gramix.io/api/v1/wallets/balance' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Accept: application/json'{
"statusCode": 201,
"data": {
"address": "UQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"memo": "a1b2c3d4e5f6g7h8"
}
}Deposits are credited asynchronously. GRAM is credited to gram, while USDT on the TON network is credited to usdt.
Recommended funding flow
POST /wallets/balance- Always include the memo in the transaction comment or forward payload. Without it, the transfer will not be credited automatically.
GET /wallets/balance
Purchases
Purchases are processed asynchronously. The API validates the request and balance, creates the order, debits the funds, and returns orderId with the processing status.
If the order moves to failed, the debited amount is returned to the same balance.
POST /api/v1/purchase/stars
Purchases between 50 and 1,000,000 Telegram Stars for the specified Telegram username.
| Headers | Required | Description |
|---|---|---|
x-api-key | yes | Authentication |
idempotency-key | yes | 8–64 |
Accept | no | application/json |
Content-Type | yes | application/json |
{
"recipientName": "telegram_user",
"paymentCurrency": "usdt",
"stars": 500
}curl --request POST 'https://api.gramix.io/api/v1/purchase/stars' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'idempotency-key: stars_20260713_0001' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
"recipientName": "telegram_user",
"paymentCurrency": "usdt",
"stars": 500
}'{
"statusCode": 201,
"data": {
"orderId": "7ba77616-7706-4215-9e6f-b6c67e4df292",
"status": "processing",
"idempotencyKey": "stars_20260713_0001"
}
}POST /api/v1/purchase/premium/{duration}
Purchases Telegram Premium for 3, 6, or 12 months.
| Headers | Required | Description |
|---|---|---|
x-api-key | yes | Authentication |
idempotency-key | yes | 8–64 |
Accept | no | application/json |
Content-Type | yes | application/json |
{
"recipientName": "telegram_user",
"paymentCurrency": "gram"
}curl --request POST 'https://api.gramix.io/api/v1/purchase/premium/6' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'idempotency-key: premium_6_20260713_0001' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
"recipientName": "telegram_user",
"paymentCurrency": "gram"
}'{
"statusCode": 201,
"data": {
"orderId": "18ca3cce-8441-4de6-874a-fd34792ae1b5",
"status": "processing",
"idempotencyKey": "premium_6_20260713_0001"
}
}{
"statusCode": 400,
"message": "Invalid premium duration. Allowed values: 3, 6, 12"
}POST /api/v1/purchase/gram
Purchases and sends between 1 and 10,000 GRAM to the specified Telegram recipient.
| Headers | Required | Description |
|---|---|---|
x-api-key | yes | Authentication |
idempotency-key | yes | 8–64 |
Accept | no | application/json |
Content-Type | yes | application/json |
{
"recipientName": "telegram_user",
"paymentCurrency": "usdt",
"gram": 2.5
}curl --request POST 'https://api.gramix.io/api/v1/purchase/gram' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'idempotency-key: gram_20260713_0001' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
"recipientName": "telegram_user",
"paymentCurrency": "usdt",
"gram": 2.5
}'{
"statusCode": 201,
"data": {
"orderId": "882dd8ca-5d68-4b85-bb26-c25ee38d9a98",
"status": "processing",
"idempotencyKey": "gram_20260713_0001"
}
}Order status webhook
If a webhookUrl is configured for the API key, the API sends a POST request after the order is completed.
POST {webhookUrl}
Content-Type: application/jsonA successful order sends an order.completed event; an unsuccessful order sends order.failed.
{
"event": "order.completed",
"orderId": "7ba77616-7706-4215-9e6f-b6c67e4df292",
"status": "completed",
"processingStatus": "completed_fragment_purchase",
"type": "stars",
"amount": "7.6500",
"currency": "usd",
"recipientName": "telegram_user",
"createdAt": "2026-07-13T10:20:30.000Z"
}{
"event": "order.failed",
"orderId": "7ba77616-7706-4215-9e6f-b6c67e4df292",
"status": "failed",
"processingStatus": "failure_fragment_purchase",
"type": "stars",
"amount": "7.6500",
"currency": "usd",
"recipientName": "telegram_user",
"createdAt": "2026-07-13T10:20:30.000Z"
}Webhook schema
| Field | Type | Constraints |
|---|---|---|
event | string | order.completed | order.failed |
orderId | UUID | — |
status | string | completed | failed |
type | string | stars | gram | premium_* |
amount | decimal(4) | — |
currency | string | gram | usd |
recipientName | string | — |
createdAt | ISO 8601 | — |
Webhook handler requirements
Return an HTTP 2xx response as quickly as possible, process events idempotently, and do not rely on their delivery order.
Complete client flow example
- Check the balance.
- If necessary, get the deposit details, fund the wallet, and wait for the balance to change.
- Create a purchase with a unique idempotency-key.
- Save orderId, status, and idempotencyKey. A 201 response means the order has been accepted for processing.
- Wait for order.completed or order.failed and match the event using orderId.
GET /api/v1/wallets/balance
x-api-key: YOUR_API_KEYPOST /api/v1/wallets/balance
x-api-key: YOUR_API_KEYPOST /api/v1/purchase/stars
x-api-key: YOUR_API_KEY
idempotency-key: order_20260713_0001
Content-Type: application/json
{
"recipientName": "telegram_user",
"paymentCurrency": "usdt",
"stars": 500
}TypeScript client example
type SuccessResponse<T> = {
statusCode: number;
data: T;
};
type ApiError = {
statusCode: number;
message: string;
};
type PurchaseResponse = {
orderId: string;
status: "created" | "processing" | "completed" | "failed" | "canceled";
idempotencyKey: string;
};
type WalletBalance = {
gram: string;
usdt: string;
};
const BASE_URL = "https://api.gramix.io";
async function apiRequest<T>(path: string, apiKey: string, init: RequestInit = {}): Promise<T> {
const response = await fetch(
`${BASE_URL}${path}`,
{
...init,
headers: {
Accept: "application/json",
"x-api-key": apiKey,
...(init.body ? { "Content-Type": "application/json" } : {}),
...init.headers,
},
},
);
const payload = (await response.json()) as SuccessResponse<T> | ApiError;
if (!response.ok) {
const error = payload as ApiError;
throw new Error(`[${error.statusCode}] ${error.message}`);
}
return (payload as SuccessResponse<T>).data;
}
export function getBalance(apiKey: string): Promise<WalletBalance> {
return apiRequest<WalletBalance>("/api/v1/wallets/balance", apiKey);
}
export function purchaseStars(
apiKey: string,
idempotencyKey: string,
input: {
recipientName: string;
paymentCurrency: "gram" | "usdt";
stars: number;
},
): Promise<PurchaseResponse> {
return apiRequest<PurchaseResponse>("/api/v1/purchase/stars", apiKey, {
method: "POST",
headers: { "idempotency-key": idempotencyKey },
body: JSON.stringify(input),
});
}Ready to start integrating?
Create an API key and configure your webhook URL.