EnraloEnralo

API for developers

Cover the TRON fee for your users' USDT transfers straight from your product: size the transfer, take a quote, execute the order, collect the result over a webhook.

What the API gives you

Quotes with a fixed price

A quote locks the price and the resource amount for an hour, so the cost of an order never changes between the estimate and the payment.

Idempotent orders

Every order accepts an Idempotency-Key: a repeated request after a timeout returns the same order instead of creating a duplicate.

Signed webhooks

Every event is signed with HMAC-SHA256, carries an event id for deduplication and is retried with a backoff until you return 2xx.

Prepaid balance

Orders are billed against a TRX balance topped up with TRX, USDT (TRC-20) or TON. Low balance triggers a balance.low event.

Sizing a transfer before you quote

POST /v1/transfer-estimate answers the question that decides everything else: how much resource this particular transfer needs. Send the wallet the USDT goes from, and the recipient if you know it. We simulate the transfer on chain, and when the sending wallet holds no USDT yet we fall back to the state of the recipient wallet. Pass from_address as target_address in the quote — the resource is delegated to the sender.

amount
What to buy, ready for the amount field of the quote. Zero when the sender already has enough. Never below the minimum order, so a tiny top-up still goes through.
purchase_needed
False means you can skip the order entirely: the sender covers this transfer on their own.
required_energy
What the whole transfer needs, before subtracting what the sender already has.
available_energy
What the sending wallet already has. Null when the node did not answer — then we assume zero and quote the full amount.
recipient_holds_usdt
Whether the recipient has held USDT before. A first transfer to an empty wallet costs the network about twice as much.
basis
Where the number came from: onchain is a simulation of your transfer, recipient is the state of the receiving wallet, worst_case means we had nothing to go on and took the expensive tariff.

Answers are cached for 20 seconds per pair of wallets, with 60 requests per minute per client. Without to_address we cannot tell a first transfer from a regular one and return the more expensive case, so pass it whenever you have it.

Integration flow

Four calls: work out how much the transfer needs, quote it, execute the quote as an order, then read the status from a webhook or by polling the order.

cURL
# 1. How much to buy for this transfer
curl -X POST https://api.enralo.com/v1/transfer-estimate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"from_address":"SENDER","to_address":"RECIPIENT"}'
# -> {"amount":65000,"required_energy":65000,"available_energy":0,
#     "purchase_needed":true,"basis":"onchain","resource_type":"energy","duration":"1h"}

# 2. Quote (amount and target_address come from the estimate)
curl -X POST https://api.enralo.com/v1/quotes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"resource_type":"energy","amount":65000,"duration":"1h","target_address":"SENDER"}'

# 3. Execute quote (idempotent)
curl -X POST https://api.enralo.com/v1/orders \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: my-unique-key-1" \
  -d '{"quote_id":"QUOTE_ID"}'

# 4. Status
curl https://api.enralo.com/v1/orders/ORDER_ID -H "Authorization: Bearer YOUR_API_KEY"
Node.js
const API = 'https://api.enralo.com';
const KEY = process.env.TRXPORTAL_API_KEY;

const call = (path, body, extraHeaders = {}) =>
  fetch(`${API}${path}`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${KEY}`, 'Content-Type': 'application/json', ...extraHeaders },
    body: JSON.stringify(body),
  }).then(r => r.json());

// sender pays the USDT, recipient receives it
async function coverTransferFee(sender, recipient) {
  const estimate = await call('/v1/transfer-estimate', {
    from_address: sender,
    to_address: recipient,
  });

  // The sender already has enough resource — nothing to buy
  if (!estimate.purchase_needed) return null;

  const quote = await call('/v1/quotes', {
    resource_type: estimate.resource_type,
    amount: estimate.amount,
    duration: estimate.duration,
    target_address: sender,
  });

  return call('/v1/orders', { quote_id: quote.quote_id }, {
    'Idempotency-Key': `cover-${sender}-${Date.now()}`,
  }); // statuses via webhook, or poll GET /v1/orders/{id}
}
Webhook
// Verify incoming webhook signature (Node.js / Express)
const crypto = require('crypto');

app.post('/webhooks/trxportal', express.raw({ type: '*/*' }), (req, res) => {
  const expected = crypto
    .createHmac('sha256', process.env.TRXPORTAL_WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');
  if (req.headers['x-tp-signature'] !== expected) return res.status(401).end();

  // Delivery headers: X-Tp-Event-Id (dedupe), X-Tp-Timestamp
  const event = JSON.parse(req.body);
  // event.event_type: order.submitted | order.delegated | order.completed |
  //                   order.failed | order.refunded | deposit.credited |
  //                   balance.low | webhook.test
  res.status(200).end();
});

Order statuses

created
The order is accepted and queued for processing.
funds_reserved
The amount is held on your balance until the order is delivered or refunded.
submitted
The order is sent to a resource provider and is waiting for delegation.
delegated
The delegation transaction is confirmed in the TRON network; the transaction hash is available.
completed
The rental period is over and the order is closed.
failed
The resource could not be delivered; the held amount is released.
refunded
The payment is returned to your balance.

Start integrating

Create an API key in the dashboard and run the first quote in a couple of minutes. The full reference is in the OpenAPI docs.

API for developers — Enralo