Gramix API v1
Utiliza esta guía como contrato para desarrollar tu integración del lado del servidor.
Información general
La API pública permite consultar el saldo, recargar la billetera y comprar Telegram Stars, Telegram Premium y GRAM.
URL base
https://api.gramix.io/api/v1Formato de los datos
- Los cuerpos de las solicitudes y las respuestas de la API utilizan application/json.
- Los valores monetarios se envían como cadenas con cuatro decimales. Para realizar cálculos exactos, utiliza tipos decimales o conserva los valores como cadenas.
- Las fechas de los webhooks utilizan el formato ISO 8601. No dependas de campos JSON desconocidos.
Métodos disponibles
| Método | Endpoint | Finalidad | Estado HTTP |
|---|---|---|---|
GET | /api/v1/wallets/balance | Devuelve el saldo actual. | 200 |
POST | /api/v1/wallets/balance | Crea o devuelve la address y el memo necesarios para recargar la billetera en la red TON. | 201 |
POST | /api/v1/purchase/stars | Compra entre 50 y 1.000.000 de Telegram Stars para el nombre de usuario de Telegram indicado. | 201 |
POST | /api/v1/purchase/premium/{duration} | Compra Telegram Premium por 3, 6 o 12 meses. | 201 |
POST | /api/v1/purchase/gram | Compra y envía entre 1 y 10.000 GRAM al destinatario de Telegram indicado. | 201 |
Autenticación
Todos los métodos requieren una clave de API activa enviada en el encabezado HTTP x-api-key.
x-api-key: YOUR_API_KEYEjemplo de solicitud
curl --request GET 'https://api.gramix.io/api/v1/wallets/balance' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Accept: application/json'Si falta el encabezado o la clave no es válida, fue revocada o está inactiva, la API devuelve 401 Unauthorized.
Formato general de las respuestas
Respuesta correcta
Una respuesta correcta contiene statusCode y data. Las solicitudes POST devuelven el estado HTTP 201.
{
"statusCode": 200,
"data": {}
}Respuesta de error
Los errores contienen statusCode y un message comprensible. La lógica del cliente debe basarse principalmente en el código de estado HTTP.
{
"statusCode": 400,
"message": "Idempotency-Key header is required"
}Errores comunes
| Estado HTTP | 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"
}Esquemas comunes
PaymentCurrency
paymentCurrency selecciona el saldo utilizado para pagar la compra. Solo se aceptan gram y usdt en minúsculas.
type PaymentCurrency = "gram" | "usdt";RecipientName
recipientName es el nombre de usuario de Telegram del destinatario sin @. Debe tener entre 5 y 32 caracteres y puede contener letras latinas minúsculas, números y _.
durov
user_123
telegram_user@durov
User123
1username
user-name
abcPurchaseRequestBase
{
"recipientName": "telegram_user",
"paymentCurrency": "usdt"
}PurchaseResponse
Todos los métodos de compra devuelven orderId, status e idempotencyKey. Un pedido nuevo suele tener el estado processing.
{
"statusCode": 201,
"data": {
"orderId": "7ba77616-7706-4215-9e6f-b6c67e4df292",
"status": "processing",
"idempotencyKey": "order_20260713_0001"
}
}type OrderStatus = "created" | "processing" | "completed" | "failed" | "canceled";Idempotencia de las compras
Todos los métodos /purchase/* requieren un encabezado idempotency-key de entre 8 y 64 caracteres.
idempotency-key: UNIQUE_REQUEST_KEY- Se admiten letras latinas, números, _ y -. La clave debe ser única para cada clave de API.
- Tras un error de red, repite la misma solicitud con la misma clave. Utiliza una clave nueva para una compra nueva.
order_20260713_0001
01J2Y5VN7CV8ZQ7DPZ9J0P6E4M
client-req-12345678Posibles errores
{
"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"
}Billeteras
GET /api/v1/wallets/balance
Devuelve el saldo actual.
| Encabezados | Obligatorio | Descripción |
|---|---|---|
x-api-key | sí | Autenticación |
Accept | no | application/json |
Ejemplo de solicitud
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
Crea o devuelve la address y el memo necesarios para recargar la billetera en la red TON.
| Encabezados | Obligatorio | Descripción |
|---|---|---|
x-api-key | sí | Autenticación |
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"
}
}Las recargas se acreditan de forma asíncrona. GRAM se acredita en gram y USDT en la red TON se acredita en usdt.
Flujo de recarga recomendado
POST /wallets/balance- Incluye siempre el memo en el comentario de la transacción o en el forward payload. Sin él, la transferencia no se acreditará automáticamente.
GET /wallets/balance
Compras
Las compras se procesan de forma asíncrona. La API valida la solicitud y el saldo, crea el pedido, descuenta los fondos y devuelve orderId con el estado processing.
Si el pedido pasa al estado failed, el importe cobrado se devuelve al mismo saldo.
POST /api/v1/purchase/stars
Compra entre 50 y 1.000.000 de Telegram Stars para el nombre de usuario de Telegram indicado.
| Encabezados | Obligatorio | Descripción |
|---|---|---|
x-api-key | sí | Autenticación |
idempotency-key | sí | 8–64 |
Accept | no | application/json |
Content-Type | sí | 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}
Compra Telegram Premium por 3, 6 o 12 meses.
| Encabezados | Obligatorio | Descripción |
|---|---|---|
x-api-key | sí | Autenticación |
idempotency-key | sí | 8–64 |
Accept | no | application/json |
Content-Type | sí | 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
Compra y envía entre 1 y 10.000 GRAM al destinatario de Telegram indicado.
| Encabezados | Obligatorio | Descripción |
|---|---|---|
x-api-key | sí | Autenticación |
idempotency-key | sí | 8–64 |
Accept | no | application/json |
Content-Type | sí | 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"
}
}Webhook de estado del pedido
Si se ha configurado un webhookUrl para la clave de API, la API envía una solicitud POST al finalizar el pedido.
POST {webhookUrl}
Content-Type: application/jsonUn pedido completado correctamente envía el evento order.completed; un pedido fallido envía 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"
}Esquema del webhook
| Campo | Tipo | Restricciones |
|---|---|---|
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 | — |
Requisitos del receptor del webhook
Devuelve una respuesta HTTP 2xx lo antes posible, procesa los eventos de forma idempotente y no dependas del orden en que se entregan.
Ejemplo completo del flujo del cliente
- Consulta el saldo.
- Si es necesario, obtén los datos de recarga, recarga la billetera y espera a que cambie el saldo.
- Crea una compra con un idempotency-key único.
- Guarda orderId, status e idempotencyKey. Una respuesta 201 significa que el pedido ha sido aceptado para su procesamiento.
- Espera order.completed u order.failed y asocia el evento mediante 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
}Ejemplo de cliente TypeScript
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),
});
}¿Todo listo para iniciar la integración?
Crea una clave de API y configura la URL de tu webhook.