PayAny Energy
My Wallets
API reference
OverviewAuthenticationPrice and termsOrdersOrder statusAccountAddresses and quick rentWebhooksErrorsExamples

Webhooks

Set a callback URL on the key (or per order with callback_url) and we POST every status change of orders placed with that key.

  • Events: order.paid, order.fulfilling, order.completed, order.failed, order.refunded — order.<status in lower case> — plus ping when support tests your endpoint.
  • Target: the order's own callback_url, else the key's. Neither set ⇒ no webhooks. A deleted key gets none.
  • Callback URLs must be public https:// URLs (at most 512 characters; no localhost, no private, loopback or reserved IP addresses) — 45212 otherwise. The host is resolved again at send time, and a private address is refused.

The request#

HTTP
POST /hooks/energy HTTP/1.1
Content-Type: application/json
User-Agent: PayAnyEnergy-Webhook/1
X-Access-Id: ek_0123456789abcdef0123
X-Timestamp: 1760000123
X-Nonce: 9f1c0b4e6a2d4c1e8b7f3a5d2c6e1f00
X-Signature: 5b2e…
X-Event: order.completed
X-Delivery-Id: 9f1c0b4e6a2d4c1e8b7f3a5d2c6e1f00
X-Seq: 42

{"event":"order.completed","seq":42,"delivery_id":"9f1c…","created_at":"2026-10-13T08:00:05Z",
 "test_mode":false,"order":{…Order…}}
Body fieldMeaning
eventorder.<status> or ping
seqgrows with every event of your key — see Ordering
delivery_ididentifies the event (32 hex); the same on every retry — dedupe on it
created_atwhen the event happened
test_modetrue for test-mode orders
orderthe full Order as it was at that moment (null for ping)
HeaderValue
X-Access-Idthe key's access id
X-TimestampUnix seconds, fresh on every attempt
X-Nonceequals delivery_id (the same on every attempt)
X-SignatureHMAC over the request — see Verify a webhook
X-Event, X-Delivery-Id, X-Seqcopies of the body fields

Acknowledge#

Answer HTTP 2xx with the body ok (also accepted: success, 0, optionally JSON-quoted, any case, surrounding whitespace ignored — or a JSON object with "code": 0).

Anything else is a failed attempt — another status, a 2xx with any other body, a redirect, or no answer within 10 seconds. Respond fast and do your work after acknowledging.

Retries#

After a failed attempt n, the next one is min(30 × 2^(n−1), 3600) seconds later: 30 s, 1 min, 2 min, 4 min, … at most 1 hour between tries, 8 tries in total. After that the event is given up and our support is alerted; support can replay it (same delivery_id).

Ordering and duplicates#

  • The same event can arrive more than once — dedupe on delivery_id (X-Delivery-Id).
  • Events can arrive out of order. seq grows with every event of your key, so for one order the event with the larger seq is the newer state. Ignore an event whose seq is lower than the last one you applied to that order.
  • When in doubt, GET /v1/orders/{order_no} returns the current state.

Verify a webhook#

The signature uses the same scheme as API requests, with your key's secret — also for Bearer keys:

string_to_sign = X-Timestamp + "\n" + X-Nonce + "\n" + "POST" + "\n" + <path+query of YOUR callback URL> + "\n" + <raw body>
  1. Read the raw body bytes, before any JSON parsing.
  2. Check X-Access-Id is your key and |now − X-Timestamp| ≤ 300.
  3. Compute the HMAC with the path (and query) of your callback URL — e.g. /hooks/energy for https://shop.example/hooks/energy — and compare it in constant time with X-Signature.
  4. Skip events whose X-Delivery-Id you already processed (retries reuse it), then answer ok.
Python
from client import verify_webhook  # docs example client, or copy the function


@app.post("/hooks/energy")
async def energy_hook(request):
    body = await request.body()
    if not verify_webhook(
        SECRET, request.headers, body, "/hooks/energy", expected_access_id=ACCESS_ID
    ):
        return Response(status_code=401)
    event = json.loads(body)
    if not already_processed(event["delivery_id"]):
        handle(event)  # newest state per order = largest seq
    return PlainTextResponse("ok")
Node.js
// Express — keep the raw bytes for the signature.
app.post('/hooks/energy', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifyWebhook(SECRET, req.headers, req.body, '/hooks/energy', { expectedAccessId: ACCESS_ID })) {
    return res.status(401).end()
  }
  const event = JSON.parse(req.body.toString('utf8'))
  if (!alreadyProcessed(event.delivery_id)) handle(event)
  res.type('text/plain').send('ok')
})
PHP
$raw = file_get_contents('php://input');
if (!energy_verify_webhook($secret, getallheaders(), $raw, '/hooks/energy', $accessId)) {
    http_response_code(401);
    exit;
}
$event = json_decode($raw, true);
if (!already_processed($event['delivery_id'])) {
    handle($event);
}
echo 'ok';

verify_webhook / verifyWebhook / energy_verify_webhook are in the example clients.

PreviousAddresses and quick rentNextErrors
PayAny Energy

TRON energy for USDT transfers — a PayAny product.

Scan to download the app
Product
  • Buy Energy
  • My Wallets
  • Pricing
  • Find an Order
  • Telegram Bot ↗
Developers
  • API Docs
  • API Keys
Company
  • FAQ
  • Terms of Service
  • Privacy Policy
  • Delete your account
  • PayAny.com ↗
© 2026 PayAny. All rights reserved.Energy is delegated on the TRON network; delivery times depend on network confirmation.